如何从带类型的对象数组中提取嵌套属性的合并类型?
问题:如何从带类型约束的数组中合并出精确键值对类型?
给定如下TypeScript代码:
interface Language { nicknames: Record<string, string>; } const languages: Language[] = [ { nicknames: { william: 'bill', }, }, { nicknames: { alexander: 'sasha', }, }, ];
能否编写一个函数,接收languages并返回如下类型:
{ william: 'bill', alexander: 'sasha' }
已知当languages未定义类型且使用as const声明数组时的实现方法,但不清楚在当前既有类型约束又未用as const的情况下如何实现。
解决方案
直接给languages标注Language[]会让TypeScript把所有元素的类型拓宽为Language,丢失了nicknames里具体的键和值的字面量信息,所以首先得调整数组的定义方式,在保留类型约束的同时让TypeScript记住字面量细节。
步骤1:用泛型函数约束数组,保留字面量类型
定义一个泛型函数,让它接收符合Language[]约束的数组,同时返回原数组的字面量类型,这样既保证了类型安全,又不会丢失具体的键值信息:
interface Language { nicknames: Record<string, string>; } // 泛型函数:约束数组元素符合Language接口,同时保留字面量类型 const createLanguages = <T extends Language[]>(languages: T) => languages; // 用这个函数创建数组,代替直接标注Language[] const languages = createLanguages([ { nicknames: { william: 'bill', }, }, { nicknames: { alexander: 'sasha', }, }, ]);
步骤2:编写合并函数并推导精确返回类型
接下来写合并函数,通过泛型提取数组中每个元素的nicknames类型,交叉合并成最终的键值对类型:
// 提取并合并所有nicknames的类型 type MergeNicknames<T extends Language[]> = T[number]['nicknames'] extends infer U ? U extends Record<string, string> ? { [K in keyof U]: U[K] } : never : never; // 实现合并逻辑 function mergeNicknames<T extends Language[]>(languages: T): MergeNicknames<T> { return languages.reduce((acc, lang) => ({ ...acc, ...lang.nicknames }), {} as MergeNicknames<T>); } // 使用示例 const merged = mergeNicknames(languages); // merged的类型会被精确推导为:{ william: 'bill'; alexander: 'sasha' }
关键说明
如果无法修改languages的定义(比如它是外部传入的、已经被标注为Language[]),那确实没办法还原出精确的键值类型——因为此时TypeScript只知道每个元素的nicknames是Record<string, string>,已经丢失了具体的键和值的字面量信息,无法逆向推导。
所以核心思路是:在定义数组时,用泛型约束代替直接的类型标注,既满足类型检查要求,又保留字面量类型细节。
内容的提问来源于stack exchange,提问作者Izhaki
相关产品推荐
相关产品推荐

