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
相关产品推荐
相关产品推荐

