如何用泛型推断对象值类型?TypeScript类型安全难题
解决方案
要同时实现枚举成员全覆盖的类型安全和函数参数类型的精确推断,可以通过「泛型约束 + as const/泛型工厂函数」的组合来实现,核心是既要约束对象必须包含所有枚举键,又要保留每个函数的具体参数类型信息,避免被索引签名抹平。
方法一:使用 satisfies 关键字(TypeScript 4.9+)
这种方式最简洁,无需额外工具函数:
- 定义枚举和约束类型
export enum Enum { A, B, C, } // 约束类型:必须包含所有 Enum 成员,且每个值都是返回 string 的函数 type EnumFunctionMap = Record<Enum, (...args: any[]) => string>;
- 定义对象并添加约束
export const obj = { [Enum.A]: () => 'example 1', [Enum.B]: (arg1: string) => 'example 2, arg1: %s', [Enum.C]: (nr: string, arg2: string) => 'example %s %s', } as const satisfies EnumFunctionMap;
as const:保留每个函数的具体参数类型,避免被自动拓宽为any[];satisfies EnumFunctionMap:强制检查对象是否包含所有枚举成员,少写任何一个枚举键都会触发类型报错。
- 定义类型安全的工具函数
const fct = <E extends keyof typeof obj>( code: E, ...args: Parameters<(typeof obj)[E]> ) => { const template = obj[code](...args); // 可在此添加字符串格式化逻辑(比如替换 %s) return template; };
调用 fct 时:
code只能传入Enum的有效值;args类型会根据code自动推断:比如传Enum.A无需参数,传Enum.C必须传两个字符串参数。
方法二:使用泛型工厂函数(兼容低版本 TypeScript)
如果项目使用 TypeScript 4.9 以下版本,可通过泛型函数实现相同效果:
export enum Enum { A, B, C, } // 泛型工厂函数,约束输入对象必须包含所有枚举成员,同时保留具体函数类型 function createEnumMap<T extends Record<Enum, (...args: any[]) => string>>(map: T): T { return map; } // 生成类型安全的对象 export const obj = createEnumMap({ [Enum.A]: () => 'example 1', [Enum.B]: (arg1: string) => 'example 2, arg1: %s', [Enum.C]: (nr: string, arg2: string) => 'example %s %s', }); // 工具函数定义和方法一完全一致 const fct = <E extends keyof typeof obj>( code: E, ...args: Parameters<(typeof obj)[E]> ) => { const template = obj[code](...args); return template; };
为什么之前的方案失效?
你尝试的 { [key in Enum]: (...args: any[]) => string } 是同构映射类型,会把所有函数的参数类型统一为 any[],丢失了每个函数的具体参数信息。而上面的方案通过泛型推断保留了每个函数的原始参数类型,同时通过 Record<Enum, ...> 确保了枚举成员的全覆盖。
内容的提问来源于stack exchange,提问作者cuaies
相关产品推荐
相关产品推荐

