TypeScript 如何为对象方法定义带类型、保签名的包装函数
类型报错的根本原因
你现在写的wrapper触发TS校验失败,问题出在泛型的设计逻辑上:
- 泛型
T extends keyof myFunctions的取值范围是'one' | 'two' | 'echo'三个键的联合,TS在检查函数内部代码时,不会把你传入的func参数和T做一对一的收窄绑定,只会把T覆盖的所有可能值展开成联合类型做校验 - 这时候
myFunctions[T]会被推导为(() => number) | ((str: string) => string)的联合类型,对应的Parameters<myFunctions[T]>就变成了[] | [str: string]的参数数组联合 - 受TS参数逆变规则的限制,联合类型的函数能接收的入参,必须是所有成员函数入参的交集,和推导出的参数数组联合类型根本不兼容,自然就会报类型错误
正确的实现方案
其实完全不需要从对象的键名反推方法类型,直接让泛型捕获被传入函数的完整签名就行,这样TS会自动和传入的函数做精准绑定,从根源上避免联合类型扩散的问题:
// 定义可被包装的函数类型边界 type AnyFunction = (...args: any[]) => any; const wrapper = <F extends AnyFunction>( func: F ) => { // 返回的包装函数会自动继承原函数的参数、返回值完整签名 return (...args: Parameters<F>): ReturnType<F> => { return func(...args); }; };
如果需要限制wrapper只能接收myFunctions对象里定义的方法,不能传入其他任意函数,可以把泛型的约束范围收窄为myFunctions里所有方法的类型联合:
const wrapper = <F extends myFunctions[keyof myFunctions]>( func: F ) => (...args: Parameters<F>): ReturnType<F> => func(...args);
实际调用效果
实现完成后,根本不需要手动给wrapper传键名当泛型,TS会自动根据你传入的方法推导完整签名,完全匹配你预期的使用场景:
- 传
myFunctions.one,返回的包装函数会自动校验为「无入参、返回number」 - 传
myFunctions.echo,返回的包装函数会自动校验为「接收1个string类型入参、返回string」
顺带一提,你之前尝试的手动传入键名当泛型的写法是完全多余的:把对应方法传给wrapper时,TS会自动完成精准的类型推导,手动指定键名反而容易引发类型不匹配。
直接按以下方式调用就能通过类型校验:
wrapper(myFunctions.one)() wrapper(myFunctions.echo)('hello')
如果出现传参不匹配的情况——比如给echo方法传数字、给one方法多塞参数,TS都会准确触发报错,和原方法的类型校验规则完全一致。
内容的提问来源于stack exchange,提问作者tome
相关产品推荐
相关产品推荐

