别再用 Parameters 乱推断了!vue-i18n 封装 t 函数的正确姿势

✨ 背景介绍

在最近写 Vue 3 项目的时候,我用了 vue-i18n 来做国际化。

在组件里用 useI18n().t()i18n.global.t() 都挺顺手的,直到有一天,我在一个普通的 .ts 工具函数中也想调用 t('xxx') 来取文案,结果就踩了坑。

于是我想着封装一下 t() 方法,抽出来统一用。

作为一名不愿意写重复代码的 TypeScript 信徒,我第一时间写下了这样的代码:

初步尝试:用 Parameters 封装

最初的写法:

ts 复制代码
import { createI18n } from 'vue-i18n'

const messages = {
  en: { hello: 'Hello' },
  zh: { hello: '你好' },
}

const i18n = createI18n({
  legacy: false,
  locale: 'zh',
  messages,
})

type I18nParams = Parameters<typeof i18n.global.t>;
const t = (...args: I18nParams): string => i18n.global.t(...args);

// 期望输出:你好
t('hello')

结果:t 报错!

"应有 3 个参数,但获得 1 个"

问题分析

🧠 为什么报错 "应有 3 个参数,但获得 1 个"

vue-i18n.t 方法有多个重载形式,可能是:

ts 复制代码
t(key: string): string
t(key: string, locale: string): string
t(key: string, values: Record<string, unknown>): string
t(key: string, values: unknown[], locale: string): string
// 等等...

所以 Parameters<typeof i18n.global.t> 推断出来的是个联合类型:

ts 复制代码
type I18nParams = [string] | [string, string] | [string, Record<string, unknown>] | ...

但是我这样写:

ts 复制代码
const t = (...args: I18nParams): string => i18n.global.t(...args);

会报错,因为 ...args 要求是一个具体的元组类型 ,但 I18nParams 是联合类型,TS 不知道你是哪一个,不能展开。

一通操作猛如虎:我选择了 bind

最终解决方案:

ts 复制代码
export const t = i18n.global.t.bind(i18n.global);
// 绑定 this,保留函数签名,类型不丢失

优点:

  • ✅ 类型安全(保留原函数的签名)
  • ✅ 没有 this 绑定问题
  • ✅ 不用手动写 overload
  • ✅ 简洁

推荐使用方式

ts 复制代码
import { t } from '@/utils/i18n';

t('common.ok');
t('user.greet', { name: '斌哥' });

总结 & 建议

  • 遇到重载的 API,谨慎使用 Parameters<>,尤其是推断出来的是联合类型
  • bind 是处理带 this 上下文 API 的最佳方式
  • 在 Vue 组件之外使用 i18n,建议封装一下 t,统一管理

插播一下:Parameters 本身没错,只是用错地方了

虽然这次我在封装 i18n.global.t 的时候,使用 Parameters<> 遇到了联合类型导致的类型报错,但其实 Parameters 在绝大多数场景下,依然是非常有用的工具类型。

举几个它非常好用的例子:

单一签名函数的参数复用

ts 复制代码
function greet(name: string, age: number): void {}

type GreetParams = Parameters<typeof greet>;
// 推断为 [name: string, age: number]

const callGreet = (...args: GreetParams) => greet(...args);

在这种没有重载的函数中,Parameters 非常稳健,能复用参数类型,减少重复定义。

简单封装函数时的"偷懒利器"

ts 复制代码
function log(message: string): void {}

type LogArgs = Parameters<typeof log>; // [message: string]

const logWithTimestamp = (...args: LogArgs) => {
  console.log(new Date(), ...args);
}

🚀 如果你在使用 vue-i18n + TypeScript 时也遇到类似的问题,希望这篇文章能帮你少踩一个坑。

如果你有更好的封装方式或不同看法,欢迎评论区一起探讨!

相关推荐
江拥羡橙1 天前
【目录-单选】鸿蒙HarmonyOS开发者基础
前端·ui·华为·typescript·harmonyos
叫我阿柒啊1 天前
从Java全栈到Vue3实战:一次真实面试中的技术探索
java·数据库·spring boot·微服务·typescript·vue3·restful
江拥羡橙1 天前
【目录-多选】鸿蒙HarmonyOS开发者基础
前端·ui·华为·typescript·harmonyos
叫我阿柒啊2 天前
从Java全栈到前端框架:一次真实的面试对话与技术解析
java·javascript·typescript·vue·springboot·react·前端开发
lypzcgf2 天前
Coze源码分析-资源库-删除提示词-前端源码
前端·typescript·react·ai应用·coze·coze源码分析·智能体平台
子兮曰2 天前
🚀99% 的前端把 reduce 用成了「高级 for 循环」—— 这 20 个骚操作让你一次看懂真正的「函数式折叠」
前端·javascript·typescript
芭拉拉小魔仙2 天前
【Vue3+TypeScript】H5项目实现企业微信OAuth2.0授权登录完整指南
javascript·typescript·企业微信
摘星编程2 天前
Cursor Pair Programming:在前端项目里用 AI 快速迭代 UI 组件
前端·人工智能·ui·typescript·前端开发·cursorai
叫我阿柒啊2 天前
从Java全栈到云原生:一场技术深度对话
java·spring boot·docker·微服务·typescript·消息队列·vue3
已读不回1432 天前
TypeScript 泛型入门(新手友好、完整详解)
typescript