You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 05:15:34