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

TypeScript:如何递归地从数组树生成元组?

要实现这个支持动态层级自动补全的元组类型,核心是借助TypeScript的递归条件类型,让类型系统能根据前序选择的节点自动推导后续的可选值。下面是完整的可复用实现方案:

1. 定义通用嵌套节点类型

先抽象一个通用的嵌套节点类型,这样我们的实现可以适配不同结构的嵌套数据(只要包含目标属性和可选的children数组):

// K: 存储节点值的属性名(比如你的场景里的'value')
// V: 节点值的类型
type NestedNode<K extends string, V extends PropertyKey> = {
  [P in K]: V;
} & {
  children?: readonly NestedNode<K, PropertyKey>[];
};

2. 构建递归动态元组类型

接下来是核心的类型逻辑,我们需要三个辅助类型来实现递归推导:

// 从单个节点中提取指定属性的值类型
type GetValueFromNode<K extends string, T extends NestedNode<K, any>> = T[K];

// 根据父节点的值,查找对应的子节点值类型集合
type GetChildrenValues<K extends string, V extends PropertyKey, Nodes extends readonly NestedNode<K, any>[]> = 
  // 精确匹配父节点
  Extract<Nodes[number], { [P in K]: V }> extends { children: readonly infer C extends NestedNode<K, any>[] }
    ? // 提取子节点的值类型
      GetValueFromNode<K, C[number]>
    : never;

// 递归生成动态元组:每一层的可选值依赖于前一层的选择
type DynamicTuple<
  K extends string,
  Nodes extends readonly NestedNode<K, any>[],
  // 默认值为根节点的所有可选值
  V extends GetValueFromNode<K, Nodes[number]> = GetValueFromNode<K, Nodes[number]>
> = 
  V extends infer CurrentV extends GetValueFromNode<K, Nodes[number]>
    ? // 两种合法路径:只选当前节点,或当前节点+子节点的递归路径
      [CurrentV] | [CurrentV, ...DynamicTuple<K, Extract<Nodes[number], { [P in K]: CurrentV }>['children'] extends readonly infer C ? C : never, GetChildrenValues<K, CurrentV, Nodes>>]
    : never;

3. 实现生成函数

声明一个泛型函数,接收你的嵌套数据和目标属性名,返回上面定义的动态元组类型:

declare const generateTuple: <K extends string, Nodes extends readonly NestedNode<K, any>[]>(
  objArray: Nodes,
  property: K,
) => DynamicTuple<K, Nodes>;

测试你的数据

用你提供的data测试,编辑器会自动补全每一步的可选值:

const data = [
  { 
    value: 'value', 
    label: 'Label', 
    children: [ 
      { 
        value: 'value.1', 
        label: 'Label.1', 
        children: [{ value: 'value.1.1', label: 'Label.1.1' }] 
      }, 
      { 
        value: 'value.2', 
        label: 'Label.2', 
        children: [{ value: 'value.2.1', label: 'Label.2.1' }] 
      } 
    ] 
  },
  { value: 'value2', label: 'Label2' }
] as const;

// 自动补全示例:
// 输入 generateTuple(data, 'value') 后,编辑器会提示所有合法路径
const validPath = ['value', 'value.1', 'value.1.1'] as const; // 类型完全匹配
const invalidPath = ['value', 'value.3']; // 类型报错,不存在该子节点

核心逻辑解释

  • Extract<Nodes[number], { [P in K]: V }>:精确匹配当前选中值对应的节点,从而获取它的children数组
  • 递归的DynamicTuple:不断展开子节点的路径,直到节点没有children为止
  • 元组分支设计:既支持只选择当前节点,也支持选择完整的嵌套路径,覆盖所有可能的选择场景

这个实现完全满足你想要的动态补全效果,每一步选择后,编辑器都会自动提示下一层的合法选项。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 20:47:40