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

如何用TypeScript实现嵌套对象属性到带点路径字符串的类型映射?

问题

想要创建一个TypeScript类型,将对象的属性映射为字符串路径:

  • 直接属性映射为属性名本身,比如"a"、"x"
  • 若属性值是对象,则同时生成该属性名,以及该对象内部属性的路径(前面加上当前属性名和圆点),比如"a.b"、"a.b.c"

示例对象:

{
  a: {
    b: {
      c: 'hello'
    }
  },
  x: {
    y: {
      z: 'hello'
    }
  }
}

期望得到的类型:

"a" | "x" | "a.b" | "a.b.c" | "x.y" | "x.y.z"

尝试的实现代码:

interface A {
  a: number
  b: {
    c: string
    d: {
      e: {
        f: 'hello'
      }
    }
  }
}

type valueof<T> = T[keyof T]
type str<T> = T extends string ? T : never
type record<T> = valueof<{
  [K in keyof T]: T[K] extends any[] ? never : T[K] extends Record<string, any> ? K | `${str<K>}.${str<record<T[K]>>}` : K
}>

该代码逻辑上看似正确,但会触发无限递归导致TypeScript崩溃,需要其他可行实现方式。

解决方案

原实现的问题在于递归没有明确的终止边界,当处理嵌套对象时,TypeScript无法确定递归何时停止,进而引发无限递归。我们可以通过限制递归仅针对非数组、非基本类型的对象,利用TypeScript的递归深度限制(默认100层,足够处理大多数场景)来实现。

以下是修正后的实现:

// 排除数组和基本类型,仅保留纯对象类型
type IsPlainObject<T> = T extends any[] ? false : T extends object ? true : false;

// 生成所有属性路径的核心类型
type ObjectPaths<T> = 
  T extends object 
    ? {
        [K in keyof T & string]: 
          IsPlainObject<T[K]> extends true
            ? K | `${K}.${ObjectPaths<T[K]>}`
            : K
      }[keyof T & string]
    : never;

代码解释

  1. IsPlainObject<T>:判断类型是否为纯对象(排除数组,因为数组虽属于object,但通常不需要展开其索引路径)。
  2. ObjectPaths<T>:
    • 先判断T是否为对象类型,非对象则返回never
    • 遍历T的所有字符串类型键K:
      • 若T[K]是纯对象,生成两种路径:当前键K,以及K拼接子对象路径的组合(${K}.${ObjectPaths<T[K]>})
      • 若T[K]不是纯对象(如基本类型、数组),仅生成当前键K
    • 最后通过[keyof T & string]提取所有路径的联合类型

测试示例

用提供的interface A测试:

interface A {
  a: number
  b: {
    c: string
    d: {
      e: {
        f: 'hello'
      }
    }
  }
}

type APaths = ObjectPaths<A>;
// 得到的类型:"a" | "b" | "b.c" | "b.d" | "b.d.e" | "b.d.e.f"

如果需要避免极端深层嵌套导致的性能问题,可手动添加递归深度限制:

type ObjectPathsWithDepth<T, Depth extends number = 10, CurrentDepth extends 0[] = []> =
  CurrentDepth['length'] extends Depth
    ? keyof T & string
    : T extends object
      ? {
          [K in keyof T & string]: 
            IsPlainObject<T[K]> extends true
              ? K | `${K}.${ObjectPathsWithDepth<T[K], Depth, [...CurrentDepth, 0]>}`
              : K
        }[keyof T & string]
      : never;

// 限制最多3层递归
type APathsLimited = ObjectPathsWithDepth<A, 3>;
// 得到:"a" | "b" | "b.c" | "b.d" | "b.d.e"

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 03:13:25