TypeScript中合并对象元组的值类型及函数返回类型定义问题
问题拆解与解决方案
这是个典型的TypeScript泛型约束导致类型信息丢失的问题,我来帮你理清原因和修复方法:
为什么会返回never?
你之前的泛型约束T extends { [key : string] : any }[]是“罪魁祸首”:
- 这个约束会让TypeScript把元组中每个具体的对象类型(比如
{a1:1,a2:2})都抹平成宽泛的{ [key: string]: any }索引签名类型,丢失了所有具体的键值字面量信息。 - 当你尝试用
T[number][keyof T[number]]或PropertyOf<T[number]>时,T[number]已经变成了{ [key: string]: any },而keyof { [key: string]: any }是string | number | symbol,但{ [key: string]: any }[string]的结果是any,再加上泛型推断的限制,TypeScript无法关联回原始的具体类型,最终只能推断出never。
修复方案:保留元组的具体类型信息
我们需要调整泛型约束,让TypeScript保留元组中每个元素的原始字面量类型,再通过分布式条件类型提取所有属性值的联合类型。
完整实现代码
const a = { a1 : 1, a2 : 2 } as const; const b = { b1 : `1`, b2 : `2` } as const; // 类型工具:提取元组中所有对象的属性值联合类型 type AllPropertyValues<T extends readonly object[]> = { [I in keyof T]: T[I][keyof T[I]] }[number]; function f<T extends readonly object[]>(...t : T): AllPropertyValues<T> { // 示例实现:随机返回任意对象的任意属性值 const randomObj = t[Math.floor(Math.random() * t.length)]; const keys = Object.keys(randomObj) as Array<keyof typeof randomObj>; const randomKey = keys[Math.floor(Math.random() * keys.length)]; return randomObj[randomKey] as AllPropertyValues<T>; } // 测试:返回类型自动推断为 1 | 2 | "1" | "2" const result = f(a, b);
关键改动说明
- 泛型约束调整:把
T extends { [key : string] : any }[]改成T extends readonly object[],这样TypeScript会完整保留元组中每个元素的具体类型,不会被宽泛的索引签名覆盖。 - 类型工具
AllPropertyValues:- 遍历元组的每个索引
I,取出对应元素T[I]的所有属性值T[I][keyof T[I]]; - 通过
[number]将元组的各个元素类型合并为联合类型,最终得到所有对象属性值的集合。
- 遍历元组的每个索引
简化写法(无需单独类型工具)
你也可以直接把返回类型内嵌到函数定义中,省去单独的类型工具:
function f<T extends readonly object[]>(...t : T): { [I in keyof T]: T[I][keyof T[I]] }[number] { // 实现逻辑同上 const randomObj = t[Math.floor(Math.random() * t.length)]; const keys = Object.keys(randomObj) as Array<keyof typeof randomObj>; const randomKey = keys[Math.floor(Math.random() * keys.length)]; return randomObj[randomKey] as ReturnType<typeof f>; }
内容的提问来源于stack exchange,提问作者hopeless-programmer
相关产品推荐
相关产品推荐

