如何解决typesafe-i18n动态调用类型化函数的TS类型错误?
解决typesafe-i18n动态翻译的TypeScript类型匹配问题
背景
我正在使用typesafe-i18n库,它会生成强类型的翻译信息与函数,简化后的示例如下:
export type MyTranslations = { Hello: (arg: { field: unknown}) => string Bye: (arg: { field: unknown, date: unknown}) => string Foo: (arg: { field: unknown}) => unknown Bar: (arg: { max: unknown}) => unknown, test: string // 示例:不是所有属性都必须是函数 } const translations: MyTranslations = { Hello: (arg: { field: unknown}) => 'hello', Bye: (arg: { field: unknown, date: unknown}) => 'bye', Foo: (arg: { field: unknown}) => 'foo', Bar: (arg: { max: unknown}) => 'bar', test: '' }
现有代码
准备代码
// 准备代码 interface MyParams { [index: string]: boolean | number | string | undefined field?: keyof MyTranslations } interface Result { transKey: keyof MyTranslations, params?: MyParams } const results: Result[] = [ { transKey: 'Hello', params: { field: 'Bye' } }, { transKey: 'Bar', params: { max: 'test' } } ] type PickByType<T, V> = { [P in keyof T as T[P] extends V | undefined ? P : never]: T[P] }
翻译函数代码
function translate(results: Result[]) { results.forEach((result: Result) => { type A = PickByType<MyTranslations, Function> type C = keyof A if(result.params) { type T = typeof result.params type Req = Required<T> const req = result.params as Req const func = translations[result.transKey] type F = typeof func const f = translations as A f[result.transKey as C](req) // 此处报错 } }) } translate(results)
问题描述
报错信息:
Argument of type 'Required<MyParams>' is not assignable to parameter of type '{ field: unknown; } & { field: unknown; date: unknown; } & { field: unknown; } & { max: unknown; }'. Property 'date' is missing in type 'Required<MyParams>' but required in type '{ field: unknown; date: unknown; }'
原因是当前代码中Result的params类型过于宽泛,TypeScript会将所有翻译函数的参数类型合并为交叉类型,要求传入的参数同时满足所有函数的参数结构,但实际每个翻译键只需要对应自身的参数结构。
解决思路
步骤1:强关联翻译键与参数类型
先提取每个翻译函数的参数类型,再重新定义Result类型,让transKey和params强绑定:
// 提取翻译函数的参数类型 type TranslationFnParams<K extends keyof MyTranslations> = MyTranslations[K] extends (...args: [infer P]) => any ? P : never; // 定义强类型的Result联合类型 type Result = { [K in keyof MyTranslations]: { transKey: K; params?: TranslationFnParams<K>; } }[keyof MyTranslations];
此时Result会自动生成每个翻译键对应正确参数的联合类型,比如{ transKey: 'Hello', params: { field: unknown } } | { transKey: 'Bye', params: { field: unknown; date: unknown } },编译期就能拦截参数不匹配的错误。
步骤2:调整翻译函数逻辑
利用TypeScript的类型推断,直接匹配每个翻译键对应的函数和参数:
function translate(results: Result[]) { results.forEach((result) => { const transFn = translations[result.transKey]; // 仅处理函数类型的翻译键 if (typeof transFn === 'function') { // 根据参数是否存在调用对应逻辑 if (result.params !== undefined) { transFn(result.params); } else { // 针对无参数的翻译函数做类型断言 (transFn as () => any)(); } } }); } translate(results);
补充说明
- 调整后的
Result类型会自动校验transKey和params的匹配关系,比如给Bar传field参数会直接报错; - 对于无需参数的翻译键(比如示例中的
test),params会被自动标记为可选,调用时无需传入参数。
内容的提问来源于stack exchange,提问作者Arikael
相关产品推荐
相关产品推荐

