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

如何实现TypeScript元组联合的‘反分布’转换?

TypeScript实现元组联合类型的“反分布”转换

我有一组不同长度的元组联合类型:

type Input =
  | [a, b, c]
  | [d, e]
  | [f, g]

想要对它们做“反分布”处理,转换成单个元组。转换后的元组中,每个位置的元素是所有输入元组对应位置元素的交集:

Transform<Input> // [a & d & f, b & e & g, c]

更多示例

  • 合并对象
// 合并对象
Transform<[ { foo: 1 } ] | [ { bar: 2 } ]> // [ { foo: 1, bar: 2 } ]
  • 忽略标签
// 忽略标签
Transform<[x: { foo: 1 } ] | [ y: { bar: 2 } ]> // [ { foo: 1, bar: 2 } ]
  • 元素为never的情况
// 合适时元素可设为never
Transform<[ number ] | [ string ]> // [ never ]
  • 支持数组类型
// 支持数组类型
Transform<{ foo: 1 }[] | { bar: 2 }[]> // { foo: 1, bar: 2 }[]
  • 支持扩展运算符
// 支持扩展运算符
Transform<[ { foo: 1 }, ...{ baz: 3 }[] ] | [ { bar: 2 }, ...{ boo: 4 }[] ]>
  // [ { foo: 1, bar: 2 }, ...{ baz: 3, boo: 4 }[] ]
  • 空元组处理
// 支持空元组
Transform<[]> // []
  • 单个元组处理
// 支持单个元组
Transform<[ 'a' ]> // [ 'a' ]

实现方案

通过递归遍历元组位置、收集对应类型取交集,同时处理扩展运算符对应的剩余元素部分:

// 拆分元组为固定元素部分和剩余数组部分
type SplitTuple<T> = T extends [...infer Fixed, ...infer Rest extends any[]] 
  ? { fixed: Fixed; rest: Rest } 
  : { fixed: []; rest: never[] };

// 对一组类型取交集
type IntersectAll<T> = T extends [infer First, ...infer Rest] 
  ? First & IntersectAll<Rest> 
  : unknown;

// 获取元组长度
type MaxLength<T extends any[]> = T extends { length: infer L } ? L : 0;
// 获取所有元组固定部分的最大长度
type GetMaxFixedLength<T> = T extends any ? MaxLength<SplitTuple<T>['fixed']> : 0;
type MaxFixedLength<T> = GetMaxFixedLength<T> extends infer M ? M : 0;

// 生成索引范围,用于遍历元组的每个位置
type Range<N extends number, Acc extends number[] = []> = Acc['length'] extends N 
  ? Acc 
  : Range<N, [...Acc, Acc['length']]>;

// 获取指定索引位置的类型,不存在则返回unknown
type GetTupleElement<T, Index extends number> = T extends any[] 
  ? Index extends keyof T ? T[Index] : unknown 
  : unknown;

// 处理固定元素部分:每个位置取所有元组对应位置的类型交集
type ProcessFixedPart<T, Indices extends number[]> = {
  [I in Indices[number]]: IntersectAll<T extends any ? GetTupleElement<SplitTuple<T>['fixed'], I> : never>;
};

// 处理剩余数组部分:取所有剩余数组元素类型的交集
type ProcessRestPart<T> = IntersectAll<T extends any ? SplitTuple<T>['rest'][number] : never>[];

// 合并固定部分和剩余部分,生成最终元组
type Transform<T> = 
  T extends any[] 
    ? SplitTuple<T>['rest'] extends never[] 
      ? T // 单个元组直接返回
      : (
        ProcessFixedPart<T, Range<MaxFixedLength<T>>> extends infer Fixed 
        ? ProcessRestPart<T> extends infer Rest 
          ? Rest extends never[] 
            ? Fixed 
            : [...Fixed, ...Rest] 
          : never 
        : never
      )
    : never;

验证实现

用示例测试上述类型:

// 基础案例
type Input1 = [a, b, c] | [d, e] | [f, g];
type Result1 = Transform<Input1>; // [a & d & f, b & e & g, c]

// 合并对象
type Result2 = Transform<[ { foo: 1 } ] | [ { bar: 2 } ]>; // [ { foo: 1 } & { bar: 2 } ]

// 忽略标签
type Result3 = Transform<[x: { foo: 1 } ] | [ y: { bar: 2 } ]>; // [ { foo: 1 } & { bar: 2 } ]

// 元素为never
type Result4 = Transform<[ number ] | [ string ]>; // [ never ]

// 数组类型
type Result5 = Transform<{ foo: 1 }[] | { bar: 2 }[]>; // ({ foo: 1 } & { bar: 2 })[]

// 扩展运算符
type Result6 = Transform<[ { foo: 1 }, ...{ baz: 3 }[] ] | [ { bar: 2 }, ...{ boo: 4 }[] ]>;
// [ { foo: 1 } & { bar: 2 }, ...({ baz: 3 } & { boo: 4 })[] ]

// 空元组
type Result7 = Transform<[]>; // []

// 单个元组
type Result8 = Transform<[ 'a' ]>; // [ 'a' ]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:27:42