TypeScript泛型推断异常:带默认参数的函数类型错误
报错原因
TypeScript 报错的核心是泛型默认值≠类型约束。你给ModifierReturn设置了默认值PromiseReturn,但这只是告诉 TS「如果调用者没显式指定ModifierReturn,就用PromiseReturn代替」,并没有限制ModifierReturn必须和PromiseReturn关联。
当你把identity作为modifier的默认值时,TS 会检查这个默认值是否能适配所有可能的ModifierReturn类型——毕竟调用者可以显式指定一个和PromiseReturn完全无关的ModifierReturn,比如:
// 显式指定 ModifierReturn 为 number,与 PromiseReturn(string)无关 asyncWrapper<string, number>(() => Promise.resolve("hello"));
此时modifier的类型要求是(original: string) => number,但identity返回的是输入的string类型,完全无法匹配number,因此 TS 会抛出类型不兼容的错误。
解决方案
方案1:安全类型断言(推荐)
既然只有在使用默认值时ModifierReturn等于PromiseReturn,可以给identity添加精准的类型断言,避免使用不安全的any:
const identity = <T>(v: T) => v; const asyncWrapper = async < PromiseReturn, ModifierReturn = PromiseReturn >( asyncAction: () => Promise<PromiseReturn>, modifier: (original: PromiseReturn) => ModifierReturn = identity as (original: PromiseReturn) => ModifierReturn ) => { const promiseResult = await asyncAction(); return modifier(promiseResult); };
这个断言是安全的:当调用者不指定ModifierReturn时,它的类型就是PromiseReturn,和identity的返回类型完全匹配;如果调用者显式指定了ModifierReturn,就必须传入对应的modifier,不会用到这个默认值。
方案2:函数重载
通过重载定义两种场景的类型,让 TS 更精准地推断返回值:
const identity = <T>(v: T) => v; // 重载1:不带 modifier,返回原 Promise 结果类型 async function asyncWrapper<PromiseReturn>( asyncAction: () => Promise<PromiseReturn> ): Promise<PromiseReturn>; // 重载2:带 modifier,返回 modifier 处理后的类型 async function asyncWrapper<PromiseReturn, ModifierReturn>( asyncAction: () => Promise<PromiseReturn>, modifier: (original: PromiseReturn) => ModifierReturn ): Promise<ModifierReturn>; // 实现函数 async function asyncWrapper<PromiseReturn, ModifierReturn = PromiseReturn>( asyncAction: () => Promise<PromiseReturn>, modifier: (original: PromiseReturn) => ModifierReturn = identity as any ) { const promiseResult = await asyncAction(); return modifier(promiseResult); }
这种方式对调用者更友好,TS 会根据是否传入modifier自动推断返回类型,实现里的as any因为有重载的类型保障,不会有类型安全问题。
方案3:泛型约束(限制返回类型)
如果你不需要modifier返回完全无关的类型,可以给ModifierReturn添加约束,让它必须是PromiseReturn的子类型:
const asyncWrapper = async < PromiseReturn, ModifierReturn extends PromiseReturn = PromiseReturn >( asyncAction: () => Promise<PromiseReturn>, modifier: (original: PromiseReturn) => ModifierReturn = identity ) => { const promiseResult = await asyncAction(); return modifier(promiseResult); };
但这个方案会限制modifier只能返回原类型或其子类型,不符合你「可以修改为任意类型」的需求,仅适合特定场景。
内容的提问来源于stack exchange,提问作者Valerio Leo

