如何让TypeScript在使用泛型初始化函数时正确推断类型?
问题描述
我定义了一个可接受特定可调用参数的函数:
type Initial<T> = T | PromiseLike<T> | (() => T) | (() => PromiseLike<T>) const usePromise = <T>(initial: Initial<T>): T => { let result: unknown = null /* ... */ return result as T }
当initial的类型为Promise<number>或PromiseLike<number>时,TypeScript能正确推断T为number。但当initial为() => PromiseLike<number>这类函数类型时,TypeScript会将T推断为PromiseLike<number>,示例如下:
// 正确推断: const initial1 = new Promise<number>(() => null) const result1 = usePromise(initial1) // result1: number (符合预期) // 错误推断: const initial2: () => PromiseLike<number> = () => new Promise(() => null) const result2 = usePromise(initial2) // result2: PromiseLike<number> (不符合预期,应该是number)
注:示例中的Promise不会决议或拒绝,此问题仅关注类型推断。需要让TypeScript在第二种示例中也能正确推断类型。
解决方案
要解决这个类型推断问题,需要调整类型定义逻辑,让TypeScript能从函数返回的PromiseLike中提取内部的目标类型,而非直接将函数返回值当作T。可以通过条件类型和递归类型推断实现:
// 辅助类型:递归提取最终的目标类型 type ExtractTarget<T> = T extends PromiseLike<infer U> ? U : T extends () => infer R ? ExtractTarget<R> : T; // 保留原Initial类型定义 type Initial<T> = T | PromiseLike<T> | (() => T) | (() => PromiseLike<T>); // 修改函数泛型逻辑,基于输入类型推导最终返回值 const usePromise = <I>(initial: I): ExtractTarget<I> => { let result: unknown = null /* ... */ return result as ExtractTarget<I> }
测试修改后的代码:
// 依然正确: const initial1 = new Promise<number>(() => null) const result1 = usePromise(initial1) // result1: number // 现在推断正确: const initial2: () => PromiseLike<number> = () => new Promise(() => null) const result2 = usePromise(initial2) // result2: number
原理说明
ExtractTarget<T>是递归条件类型:- 若
T是PromiseLike<U>,则提取内部的U作为目标类型; - 若
T是函数类型,则递归处理函数的返回值; - 其他情况直接返回
T本身。
- 若
- 函数
usePromise改用泛型I接收输入参数的原始类型,通过ExtractTarget<I>推导最终返回类型,让TypeScript自动解析嵌套的函数和Promise结构,得到正确的目标类型。
内容的提问来源于stack exchange,提问作者user14325
相关产品推荐
相关产品推荐

