如何实现兼容函数联合类型的TypeScript函数包装器?
解决TypeScript函数联合类型包装器的参数类型匹配问题
你的核心问题在于:当泛型TFn被推断为整个FuncUnion联合类型时,Parameters<TFn>[0]会计算出number & string(即never),导致内部调用f(params)时类型不兼容。但实际场景中你需要的是,传入具体的单个函数时,params能自动匹配该函数的参数类型。
下面提供两种可行的解决方法:
方法一:利用分布式条件类型提取参数
通过TypeScript的分布式条件类型,针对联合类型中的每个函数成员单独提取其参数类型,确保params与传入的具体函数参数严格匹配:
type FuncUnion = ((n: number) => string) | ((s: string) => string); const fnWrapper = <TFn extends FuncUnion>( f: TFn, params: TFn extends (p: infer P) => string ? P : never ) => { f(params as Parameters<TFn>[0]); }; // 合法调用,类型推断正常 fnWrapper((a: number) => 'r', 2); fnWrapper((a: string) => 'r', 'hello'); // 传入联合类型函数时会正确报错(符合类型安全要求) // const f: FuncUnion = Math.random() > 0.5 ? (n: number) => n.toString() : (s: string) => s; // fnWrapper(f, 123); // 错误:类型“number”不能赋给类型“never”
这里的条件类型TFn extends (p: infer P) => string ? P : never会遍历FuncUnion中的每个成员,分别提取对应的参数类型,避免了联合类型交集导致的never问题。
方法二:使用函数重载
如果FuncUnion的成员数量有限,可以直接为每个成员定义重载签名,让TypeScript根据传入的函数匹配对应的参数类型:
type FuncUnion = ((n: number) => string) | ((s: string) => string); // 重载签名:对应联合类型中的每个函数 function fnWrapper(f: (n: number) => string, params: number): void; function fnWrapper(f: (s: string) => string, params: string): void; // 实现签名:接受联合类型的函数和参数 function fnWrapper(f: FuncUnion, params: number | string) { f(params as any); } // 合法调用 fnWrapper((a: number) => 'r', 2); fnWrapper((a: string) => 'r', 'hello'); // 非法调用会被TypeScript拦截 // fnWrapper((a: number) => 'r', 'hello'); // 错误:类型“string”不能赋给类型“number”
这种方式的类型提示更直接,但如果FuncUnion后续扩展新的函数成员,需要同步新增重载签名。
内容的提问来源于stack exchange,提问作者juzie
相关产品推荐
相关产品推荐

