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

TypeScript中解构后如何保留数组的联合元组类型?

TypeScript联合元组数组map后类型保留方案

先看这个场景:

const entries = [["a", 1], ["b", 2]] as (["a", 1] | ["b", 2])[];
const mapped = entries.map((e) => e);

这里entries是联合元组数组,mapped会完整保留原类型(["a", 1] | ["b", 2])[]。

但如果在map回调里解构元组:

const mapped = entries.map(([k, v]) => [k, v] as const);

此时mapped的类型会变成["a" | "b", 1 | 2][]——TypeScript会把联合元组的成员类型合并,丢失了原有的元组配对关系。

我们需要让map的输出类型和输入保持一致的联合元组结构,同时支持对每个元组成员做强类型转换,比如:

// 基于内置Uppercase工具类型
entries.map(([k, v]) => [k.toUpperCase(), v] as [Uppercase<typeof k>, typeof v]);
// 预期输出类型:(["A", 1] | ["B", 2])[]

解决方案

方案1:自定义Array.map的类型签名(优先)

TypeScript默认的Array.map类型签名没有针对联合类型做分布式处理,我们可以扩展全局Array接口,让它支持保留联合元组的个体类型:

declare global {
  interface Array<T> {
    map<U>(
      callbackfn: <Item extends T>(value: Item, index: number, array: T[]) => U,
      thisArg?: any
    ): { [Idx in keyof this]: ReturnType<typeof callbackfn> extends (arg: this[Idx]) => infer R ? R : never };
  }
}

这个签名通过泛型Item extends T让回调函数针对联合类型的每个成员单独处理,再通过索引类型映射把每个元素的转换结果对应到数组的每个位置,最终保留联合元组的结构。

方案2:在回调函数中使用泛型约束

如果不想修改全局类型,可以直接在map的回调里定义泛型,明确约束每个元素是原数组的联合成员:

const mapped = entries.map(<T extends (typeof entries)[number]>([k, v]: T) => 
  [k.toUpperCase(), v] as [Uppercase<T[0]>, T[1]]
);

这里T extends (typeof entries)[number]让每个回调处理的是联合类型中的单个元组,而非整个联合,因此Uppercase<T[0]>和T[1]会保留原元组的配对关系,最终输出类型就是(["A", 1] | ["B", 2])[]。

原理说明

TypeScript对联合类型的处理默认是合并宽化,当回调函数接收的是整个联合类型时,会把所有成员的同位置类型合并成联合;而通过泛型让回调针对每个联合成员单独处理,就能触发分布式条件类型的特性,保留每个元组的具体结构,从而让输出数组的类型保持原联合元组的配对关系。

内容的提问来源于stack exchange,提问作者3x071c

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 06:00:10