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

如何在TypeScript中按ID追踪嵌套对象的完整路径类型?

问题:根据ID查找嵌套对象并生成完整路径类型

背景与需求

定义如下嵌套对象结构:

type IObject = { id: string, path: string, children?: IObject[] }

const tree = [
    {
      id: 'obj1' as const,
      path: 'path1' as const,
      children: [
        {
          id: 'obj2' as const,
          path: ':pathParam' as const,
          children: [
            {
              id: 'obj3' as const,
              path: 'path3' as const
            }
          ]
        },
        {
          id: 'obj4' as const,
          path: 'path4' as const,
          children: [
            {
              id: 'obj5' as const,
              path: 'path5' as const
            }
          ]
        }
      ]
    }
];

需要实现类型FindAndTrackDeepPath<ID, T>,根据指定ID从树结构T中找到对应对象类型,同时附加fullPath字段存储根到该对象的完整路径(路径片段用/拼接)。例如:

type ok = FindAndTrackDeepPath<'obj2', typeof tree>['fullPath'];
// 期望类型:"/path1/:pathParam"

原实现的问题

尝试的实现如下,但查找深层节点时会生成错误的联合路径:

export type FindAndTrackDeepPath<
  ID,
  T extends IObject[],
  depth extends number = 1,
  path extends string = '',
  maxDepth extends number = 10
> = depth extends maxDepth
  ? never
  : FindByID<ID, T> extends never
    ? FindAndTrackDeepPath<
        ID,
        Descend<T>['children'],
        Increment<depth>,
        Descend<T>['path'] extends undefined ? '' : `${path}/${Descend<T>['path']}`
      >
    : FindByID<ID, T> & { fullPath: `${path}/${FindByID<ID, T>['path']}` };

// 辅助类型
type FindByID<ID, T extends IObject[]> = Extract<T[number], { id: ID }>;
type Descend<T extends IObject[]> = Extract<T[number], { children: IObject[] }>;
type Increment<N extends number> = [1,2,3,4,5,6,7,8,9,10,11,12,...number[]][N];

// 错误示例
type notOK = FindAndTrackDeepPath<'obj3', typeof tree>['fullPath'];
// 实际得到:"/path1/:pathParam/path3" | "/path1/path4/path3",其中第二个路径是错误的

问题根源:Descend<T>会提取当前层级所有带children的节点形成联合类型,递归时会同时遍历所有分支的children,导致路径拼接时混入不相关分支的path,生成错误的联合结果。

正确实现方案

改为深度优先、单分支递归:针对每个节点单独处理,要么找到目标ID返回路径,要么递归遍历该节点的children并传递当前已拼接的路径。

实现代码:

type IObject = { id: string, path: string, children?: IObject[] };

// 处理单个节点的逻辑
type FindInNode<ID, Node extends IObject, CurrentPath extends string> = 
  // 当前节点是目标,返回带完整路径的类型
  Node['id'] extends ID 
    ? Node & { fullPath: `${CurrentPath}/${Node['path']}` }
    : // 当前节点有children,递归遍历子节点
      Node['children'] extends infer C extends IObject[]
        ? FindAndTrackDeepPath<ID, C, `${CurrentPath}/${Node['path']}`>
        : never;

// 入口类型:遍历数组中的每个节点
type FindAndTrackDeepPath<ID, T extends IObject[], CurrentPath extends string = ''> = 
  T[number] extends infer Node extends IObject
    ? FindInNode<ID, Node, CurrentPath>
    : never;

// 测试用例
const tree = [
    {
      id: 'obj1' as const,
      path: 'path1' as const,
      children: [
        {
          id: 'obj2' as const,
          path: ':pathParam' as const,
          children: [
            {
              id: 'obj3' as const,
              path: 'path3' as const
            }
          ]
        },
        {
          id: 'obj4' as const,
          path: 'path4' as const,
          children: [
            {
              id: 'obj5' as const,
              path: 'path5' as const
            }
          ]
        }
      ]
    }
] as const;

type ok1 = FindAndTrackDeepPath<'obj2', typeof tree>['fullPath'];
// type ok1 = "/path1/:pathParam"

type ok2 = FindAndTrackDeepPath<'obj3', typeof tree>['fullPath'];
// type ok2 = "/path1/:pathParam/path3"

type ok3 = FindAndTrackDeepPath<'obj5', typeof tree>['fullPath'];
// type ok3 = "/path1/path4/path5"

实现逻辑说明

  1. FindInNode:负责单个节点的匹配与递归:
    • 若当前节点ID匹配目标,返回该节点类型并附加拼接完成的fullPath。
    • 若当前节点有子节点,递归调用FindAndTrackDeepPath处理子节点数组,同时传递当前已拼接的路径。
  2. FindAndTrackDeepPath:作为入口遍历输入数组的每个节点,调用FindInNode处理。由于ID唯一,最终只会返回匹配的单个分支结果,不会生成错误的联合类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 13:55:25