TypeScript:为绑定变量后的函数映射配置正确类型
解决TypeScript中函数映射绑定后的类型推断问题
我懂你的困扰:你想给一组共享同一绑定变量的函数映射配置自动类型推断,单个函数的绑定(比如performBind)能正常识别类型,但处理整个映射的associateActions返回的函数却丢失了具体签名,被推断成(...args: any[]) => void,导致没有代码补全和类型提示。
问题根源
原来的类型定义用了统一的泛型F来约束映射里的所有函数,但这种方式没让TypeScript跟踪到映射中每个键对应的具体函数签名。ActionMapping和PostActionMapping把整个映射的函数都统一成F类型,再加上associateActions循环处理时的类型模糊,最终导致TypeScript无法精准关联每个键的函数类型和返回的映射类型,丢失了类型信息。
解决方案
我们需要调整泛型逻辑,让类型系统直接基于输入的函数映射结构,为每个键单独生成绑定后的函数类型:
// 保留原有的参数尾元组提取逻辑 type Tail<T extends any[]> = ((...args: T) => any) extends ((head: any, ...tail: infer R) => any) ? R : never; // 重构输出类型:基于输入的函数映射A,为每个键生成绑定后的函数签名 type PostActionMapping<A extends Record<string, (...args: any[]) => any>> = { [K in keyof A]: (...args: Tail<Parameters<A[K]>>) => ReturnType<A[K]>; }; // 单个绑定函数保持原有逻辑不变 function performBind<F extends (...args: any) => any>(func: F, binding: any): (...args: Tail<Parameters<F>>) => ReturnType<F> { return func.bind(null, binding); } // 重构映射处理函数:仅接收函数映射A,自动推断每个函数的具体类型 function associateActions<A extends Record<string, (...args: any[]) => any>>( actions: A, toBind: Parameters<A[keyof A]>[0] // 确保绑定值类型与所有函数第一个参数类型匹配 ): PostActionMapping<A> { const actionList = {} as PostActionMapping<A>; Object.keys(actions).forEach(key => { const func = actions[key as keyof A]; if (typeof func === 'function') { // 这里的类型断言是安全的,因为我们明确基于原函数类型生成了目标类型 actionList[key as keyof A] = performBind(func, toBind) as PostActionMapping<A>[keyof A]; } }); return actionList; }
关键改进点
PostActionMapping直接依赖输入映射类型:不再用统一的F,而是遍历输入映射A的每个键,基于该键对应的原函数类型生成绑定后的函数类型,让TypeScript能精准跟踪每个函数的签名。- 约束绑定值的类型:
toBind的类型被设置为所有函数第一个参数的交集,确保你传入的绑定值能匹配所有函数的第一个参数类型,避免类型不兼容的问题。 - 简化泛型参数:
associateActions只需要接收函数映射A,不需要额外的F参数,让类型推断更自然流畅。
测试验证
用你的测试代码验证效果:
// 单个函数绑定测试(依然正常工作) const testing = performBind((numberArg: number, stringArg: string): string => { return `${numberArg} - ${stringArg}`; }, 3); testing('test'); // 正确推断为 (stringArg: string) => string // 函数映射测试 const myActions = { testing: (numberArg: number, stringArg: string): string => { return `${numberArg} - ${stringArg}`; }, // 可以添加更多不同签名的函数,只要第一个参数是number即可 anotherAction: (id: number, isActive: boolean): boolean => { return id > 0 && isActive; } }; const mapped = associateActions(myActions, 3); mapped.testing('test'); // 正确推断为 (stringArg: string) => string mapped.anotherAction(true); // 正确推断为 (isActive: boolean) => boolean
现在mapped里的每个函数都会有正确的类型提示和代码补全,功能也和之前保持一致。
内容的提问来源于stack exchange,提问作者Dave W
相关产品推荐
相关产品推荐

