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

TypeScript中泛型为何仅提取对象首个函数的返回值类型

TypeScript 泛型未正确推断返回值联合的原因

问题场景复现

存在如下多函数属性的handlers对象:

const handlers = {
  handler1: (state: State, payload: boolean) => ({
    schema: payload,
    errors: [],
  }),
  handler2: (state: State, payload: string[]) => ({
    errors: payload,
  }),
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  reset: (...args: any) => initialState,
}

需求是合并对象内所有函数的返回值,得到预期联合类型:

State | {
  schema: boolean,
  errors: never[]
} | {
  errors: string[]
}

以下索引访问写法可以正常得到预期结果:

type MergeReturnTypes1 = ReturnType<typeof handlers[keyof typeof handlers]>

但以下自定义泛型传入typeof handlers时,仅返回第一个函数的返回值类型,未生成联合:

type MergeReturnType2<H> = H extends Record<string, (...args: any) => infer T> ? T : never

type T = MergeReturnType2<typeof handlers>
// T 实际结果: { schema: boolean; errors: never[] }

问题根本原因

1. 条件类型分布式特性未触发

TypeScript 条件类型的分布式分发逻辑,仅在extends关键字左侧是**裸类型参数(即没有被数组、泛型、对象包裹的泛型联合类型)**时才会生效。
在MergeReturnType2中,extends左侧是整个对象类型H,H本身不是联合类型,自然不会触发分发遍历逻辑。

2. Record<string, V>的infer推断规则不做属性遍历

Record<string, (...args: any) => infer T>的匹配语义是:「对象所有string类型键对应的属性,都必须是返回T类型的函数」,这里的T是一个统一的单一类型,不是联合类型。
当传入有固定已知键的字面量对象(比如typeof handlers的键是'handler1'|'handler2'|'reset'固定字面量联合)时,TypeScript 不会自动遍历每个属性收集返回值生成联合,只会尝试推导一个能满足所有属性返回值约束的公共类型。由于三个handler的返回值没有子类型继承关系,TS的类型推断器会取第一个匹配到的属性返回值作为结果,不会做联合合并。
而能正常工作的MergeReturnTypes1,逻辑是先通过keyof typeof handlers拿到所有键的联合,再通过索引访问拿到所有函数类型的联合,ReturnType作用于函数联合类型时本身是分布式的,会自动返回所有函数返回值的联合,因此能得到正确结果。

正确自定义泛型写法

需要显式遍历对象所有属性收集返回值,两种常用实现:

  • 方式1:先取所有属性值的函数联合,再统一取返回值
type MergeReturnTypeCorrect<H extends Record<string, (...args: any) => any>> = ReturnType<H[keyof H]>
  • 方式2:通过映射类型遍历每个键单独推断返回值,再取索引联合
type MergeReturnTypeCorrect2<H> = {
  [K in keyof H]: H[K] extends (...args: any) => infer R ? R : never
}[keyof H]

两种写法都会显式触发对象属性遍历,最终得到所有函数返回值的联合类型。


内容的提问来源于stack exchange,提问作者Antonio Santoro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 21:12:41