TypeScript泛型函数指定类型参数后返回类型异常问题排查
TypeScript泛型函数指定部分泛型参数后返回类型异常问题
我定义了一个接收回调函数作为参数的泛型函数,它会返回回调函数的执行结果,预期返回类型与回调函数的返回类型一致:
type Options<Data, Error> = { /* 此处为Options类型定义 */ }; const useMethod = < Data, Error = unknown, TReturn = unknown, >( fn: (options?: Options<Data, Error>) => TReturn, options?: Options<Data, Error>, ): TReturn => { return fn(options); }; const callback1 = () => { return "Hello" } const callback2 = () => { return {hello: "World"} } const callback3 = () => { return 100; } useMethod(callback1); // 类型为string,符合预期 useMethod(callback2); // 类型为{hello: string},符合预期 useMethod(callback3); // 类型为number,符合预期
但当手动指定第一个泛型参数Data时,函数返回类型变成了默认的unknown,而非回调函数的实际返回类型:
useMethod<{ param: string; }>(callback1); // 期望为string,实际为unknown useMethod<string>(callback2); // 期望为{hello: string},实际为unknown useMethod<boolean>(callback3); // 期望为number,实际为unknown
问题原因
TypeScript的泛型参数推导规则是:当你手动指定部分泛型参数时,未指定的参数如果有默认值,就会直接使用默认值,不会再尝试从函数参数中自动推导。
在原定义中,TReturn的默认值是unknown,当你指定Data后,TReturn没有被手动指定,也不会被自动推导,直接使用了默认的unknown,导致返回类型不符合预期。
解决方法
方法1:使用TypeScript 4.7+的部分泛型参数推断(占位符_)
TypeScript 4.7及以上版本支持用_作为占位符,让编译器自动推导对应的泛型参数。调用时只指定需要的Data,其余参数用_占位:
useMethod<{ param: string }, _, _>(callback1); // 返回类型为string,符合预期 useMethod<string, _, _>(callback2); // 返回类型为{hello: string},符合预期 useMethod<boolean, _, _>(callback3); // 返回类型为number,符合预期
方法2:重新设计泛型参数,优先推导回调函数的返回类型
调整泛型定义,让回调函数的类型被优先推断,通过ReturnType获取其返回类型,同时保留手动指定Data和Error的能力:
type Options<Data, Error> = { /* 此处为Options类型定义 */ }; const useMethod = < Data, Error = unknown, Fn extends (options?: Options<Data, Error>) => unknown = (options?: Options<Data, Error>) => unknown, >( fn: Fn, options?: Options<Data, Error>, ): ReturnType<Fn> => { return fn(options) as ReturnType<Fn>; };
此时调用时指定Data,Fn会自动从传入的回调函数推断,返回类型也会正确对应回调的返回值:
useMethod<{ param: string }>(callback1); // 返回类型为string useMethod<string>(callback2); // 返回类型为{hello: string} useMethod<boolean>(callback3); // 返回类型为number
方法3:调整泛型参数顺序(低版本兼容方案)
如果无法使用高版本TypeScript,可以调整泛型参数顺序,将需要自动推导的TReturn放在最前面,手动指定Data时同时显式声明TReturn:
const useMethod = < TReturn, Data = unknown, Error = unknown, >( fn: (options?: Options<Data, Error>) => TReturn, options?: Options<Data, Error>, ): TReturn => { return fn(options); }; // 调用时需要同时指定TReturn和Data useMethod<string, { param: string }>(callback1); // 返回类型为string
这种方式需要手动声明返回类型,不够灵活,仅作为低版本TypeScript的兼容方案。
内容的提问来源于stack exchange,提问作者Lwyrn
相关产品推荐
相关产品推荐

