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

解决TypeScript中点表示法对象路径递归类型过深问题

解决TypeScript递归对象路径类型的无限深度报错问题

我正在实现用点表示法(如users.1.name)表示对象路径的TypeScript类型定义,当前方案在普通对象上可以正常工作,但处理递归类型时会触发错误:

Type instantiation is excessively deep and possibly infinite.

以下是原实现代码:

type PathImpl<T, K extends keyof T> =
  K extends (string | number)
  ? T[K] extends Record<string, any>
    ? T[K] extends ArrayLike<any>
      ? K | `${K}.${PathImpl<T[K], number>}`
      : K | `${K}.${PathImpl<T[K], keyof T[K]>}`
    : K
  : never;

export type ObjectPath<T> = PathImpl<T, keyof T> | keyof T;

// 普通对象测试正常
type User = {
  name: string,
  address: {
    lines: {
      street: string,
      streetNumber: number
    }[],
    zip: string,
  }
};

const streetNumber: ObjectPath<User> = "address.lines.2.streetNumber" // ✅
const foo: ObjectPath<User> = "address.lines.2.foo" // ❌ 正确拒绝无效路径

// 递归类型触发报错
type UserWithFriends = {
  name: string,
  friends: UserWithFriends[]
}

// TS Error: Type instantiation is excessively deep and possibly infinite.
const f: ObjectPath<UserWithFriends> = 'name'

问题原因

原类型PathImpl是无限制递归的,当遇到UserWithFriends这种自引用的递归结构时,TypeScript编译器会不断尝试展开嵌套的类型,超出了内置的递归深度限制,从而抛出错误。


解决方案1:限制递归深度

通过引入一个深度参数,当递归达到指定层数后停止展开,避免无限递归。

// 可自定义最大递归深度,默认10层
type PathImpl<T, K extends keyof T, Depth extends number = 10, CurrentDepth extends number[] = []> =
  // 达到最大深度时停止递归
  CurrentDepth['length'] extends Depth
  ? K
  : K extends (string | number)
  ? T[K] extends Record<string, any>
    ? T[K] extends ArrayLike<any>
      ? K | `${K}.${PathImpl<T[K], number, Depth, [...CurrentDepth, 0]>}`
      : K | `${K}.${PathImpl<T[K], keyof T[K], Depth, [...CurrentDepth, 0]>}`
    : K
  : never;

export type ObjectPath<T, Depth extends number = 10> = PathImpl<T, keyof T, Depth> | keyof T;

// 测试递归类型
type UserWithFriends = {
  name: string,
  friends: UserWithFriends[]
}

const f: ObjectPath<UserWithFriends> = 'name' // ✅
const friendName: ObjectPath<UserWithFriends> = 'friends.0.name' // ✅
// 支持最多10层嵌套路径,超过则停止展开

优点:可控性强,明确知道最大支持的路径层数,适合对深度有预期的业务场景;缺点:极端超深度场景下可能无法覆盖,但日常开发足够使用。


解决方案2:自动检测递归引用

利用TypeScript条件类型的特性,当检测到类型递归引用自身时停止展开,避免无限递归。

type PathImpl<T, K extends keyof T> =
  K extends (string | number)
  ? T[K] extends Record<string, any>
    ? // 检测是否递归引用自身,是则停止递归
      T[K] extends T
      ? K
      : T[K] extends ArrayLike<any>
        ? K | `${K}.${PathImpl<T[K], number>}`
        : K | `${K}.${PathImpl<T[K], keyof T[K]>}`
    : K
  : never;

export type ObjectPath<T> = PathImpl<T, keyof T> | keyof T;

// 测试递归类型
type UserWithFriends = {
  name: string,
  friends: UserWithFriends[]
}

const f: ObjectPath<UserWithFriends> = 'name' // ✅
const friendName: ObjectPath<UserWithFriends> = 'friends.0.name' // ✅
const deepFriend: ObjectPath<UserWithFriends> = 'friends.0.friends.0.name' // ✅

优点:更灵活,无需手动设置深度,能自动处理合理的递归嵌套;缺点:对于极端复杂的递归结构,仍可能触发编译器深度限制,但足以应对绝大多数日常开发场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 04:35:29