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

如何从带类型的对象数组中提取嵌套属性的合并类型?

问题:如何从带类型约束的数组中合并出精确键值对类型?

给定如下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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 05:07:27