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

保留元组类型而非联合类型数组

保留元组类型而非联合类型数组

嘿,我来帮你搞定这个扁平化数组还能保留精确元组类型的需求!

首先先看你给出的原始代码:

type SortOptions<S> = (
  | {
      label: string;
      children: S[];
    }
  | S
)[];

const sortOptions = [
  "Active",
  "Hot",
  {
    label: "Top",
    children: ["TopHour", "TopSixHour"],
  },
  "New",
  "Controversial",
] as const;

你的核心需求应该是:把这个带嵌套结构的元组扁平化后,得到的类型是精确的["Active", "Hot", "TopHour", "TopSixHour", "New", "Controversial"],而不是宽泛的联合类型数组(比如string[]或者包含对象的联合数组)。

要实现这个,我们需要用递归的元组类型推导来替代普通的数组类型处理,具体步骤如下:

1. 定义递归的扁平化类型工具

先写两个类型工具,一个用来处理单个元素的扁平化,另一个用来遍历整个元组拼接结果:

// 处理单个元素:如果是带children的对象,就递归扁平化它的children;否则返回单元素元组
type FlattenElement<T> = T extends { children: infer C extends readonly any[] } 
  ? FlattenTuple<C> 
  : [T];

// 遍历元组的每一项,把每一项扁平化后的结果拼接成新元组
type FlattenTuple<T extends readonly any[]> = T extends [infer First, ...infer Rest]
  ? [...FlattenElement<First>, ...FlattenTuple<Rest>]
  : [];

2. 实现对应的扁平化函数

然后写一个函数,让它的返回类型自动推导为我们定义的FlattenTuple类型:

function flattenSortOptions<T extends readonly any[]>(options: T): FlattenTuple<T> {
  const result: any[] = [];
  for (const item of options) {
    // 判断当前项是否有children属性,有就递归处理children,没有直接推入结果
    if ("children" in item) {
      result.push(...flattenSortOptions(item.children));
    } else {
      result.push(item);
    }
  }
  // 这里用类型断言告诉TS,结果就是我们推导的精确元组类型
  return result as FlattenTuple<T>;
}

3. 测试效果

现在调用这个函数,你会发现返回值的类型完全是你想要的精确元组:

const flattened = flattenSortOptions(sortOptions);
// flattened的类型是:
// readonly ["Active", "Hot", "TopHour", "TopSixHour", "New", "Controversial"]

完美!这样就不会把类型变成模糊的联合数组,而是严格保留了扁平化后的元组结构。如果不需要只读的元组类型,你可以在FlattenTuple里去掉readonly相关的约束,或者在返回时断言为FlattenTuple<T> & any[],不过一般建议保留只读属性,和原始的as const元组保持一致。

备注:内容来源于stack exchange,提问作者aeharding

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 16:28:00