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

TypeScript中如何将元组对象映射为对象类型?

TypeScript类型转换问题解决:Config转Instance

问题背景

定义了如下Config类型:

type Config = { endpoints: [{name: "abc", created: false}, {name: "xyz", created: true}] }

需要转换为目标Instance类型:

type Instance = { 
  abc: {name: "abc", created: false}, 
  xyz: {name: "xyz", created: true} 
}

原编写的Transform类型出现**"name无法作为T['endpoints'][K[key]]的索引"**错误;添加数字类型守卫后错误消失,但Instance类型变为unknown。

原错误代码:

type IntersectOf<U extends any> = (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

type Transform<T extends Config, K extends (keyof T)[] = (keyof T)[]> = IntersectOf<{
  [key in keyof K]:
    K[key] extends keyof T['endpoints']
    ? Record<T['endpoints'][K[key]]['name'], T['endpoints'][K[key]]>
    : never;
}[number]>;

type Instance = Transform<Config>;

添加类型守卫后的代码(结果为unknown):

type Trasnform<T extends Config, K extends (keyof T)[] = (keyof T)[]> = IntersectOf<{
  [key in keyof K]:
    K[key] extends keyof T['endpoints']
    ? K[key] extends number
    ? Record<T['endpoints'][K[key]]['name'], T['endpoints'][K[key]]>
    : never
    : never;
}[number]>;

错误原因分析

  1. 原代码中K extends (keyof T)[]约束错误:keyof T是"endpoints",所以K被限定为["endpoints"]数组,和遍历endpoints数组索引的需求完全不匹配,导致后续类型推导失败。
  2. 添加数字守卫后,由于K的类型是["endpoints"],K[key]永远不会是number,所有分支返回never,最终IntersectOf处理never得到unknown。

正确实现方案

直接利用TypeScript的重映射类型,遍历endpoints数组的元素类型,将元素的name作为键,元素本身作为值:

type Config = { endpoints: [{name: "abc", created: false}, {name: "xyz", created: true}] }

type Transform<T extends { endpoints: readonly { name: string }[] }> = {
  [E in T['endpoints'][number] as E['name']]: E;
};

type Instance = Transform<Config>;
// 最终结果:{ abc: { name: "abc"; created: false; }, xyz: { name: "xyz"; created: true; } }

方案说明

  • T['endpoints'][number]提取endpoints数组所有元素的联合类型:{name: "abc", created: false} | {name: "xyz", created: true}
  • as E['name']是重映射语法,将联合类型中每个元素的name属性作为新对象的键
  • 直接将元素E作为对应键的值,一步得到目标类型

替代实现(保留IntersectOf思路)

如果想沿用原有的交叉合并思路,需要修正K的约束逻辑,正确提取数组元素类型:

type IntersectOf<U> = (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

type Transform<T extends { endpoints: readonly any[] }> = IntersectOf<
  T['endpoints'] extends readonly (infer E)[]
  ? E extends { name: infer N extends string }
    ? Record<N, E>
    : never
  : never
>;

type Instance = Transform<Config>;
// 结果同样符合预期

修正说明

  • 将T约束为包含endpoints数组的类型,通过infer E提取数组元素类型E
  • 约束E必须包含字符串类型的name属性,生成对应的Record类型
  • 用IntersectOf将所有Record类型交叉合并,得到最终的对象类型

内容的提问来源于stack exchange,提问作者Nauman Umer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 23:26:05