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

TypeScript递归类型实例化过深(TS2589)错误的优化方案咨询

TypeScript递归类型报错:Type instantiation is excessively deep and possibly infinite.ts(2589)

问题描述

VS Code提示错误:

Type instantiation is excessively deep and possibly infinite.ts(2589)

我实现了一个递归类型NestedObjectValue,用于根据点分隔的路径字符串(如"grandparent.parent.child")获取嵌套对象的对应值,代码如下:

export type NestedObjectValue<
  T extends Record<string, any>,
  K extends Readonly<string>
> =
  K extends `${infer F extends string}.${infer R extends string}`
    ? NestedObjectValue<T[F], R>
    : K extends keyof T
      ? T[K]
      : never

我的业务模型规模较大(包含600+记录),且存在无限递归结构,例如:

type Item = {
   "name": string,
   "children": Item[]
}

请问:

  1. 这个NestedObjectValue类型是否存在明显的低效问题?
  2. 有哪些方案可以避免上述TypeScript错误?

回答

一、原类型的低效问题

原类型采用深度递归实现,每次拆分路径片段都会触发一次类型递归实例化。对于长路径(多层嵌套)或无限递归的业务模型(如Item),TypeScript会持续递归展开类型,很快就会触达类型系统的递归深度上限,这就是报错的直接原因。同时,这种深度递归的类型计算效率极低,大规模模型下会显著拖慢TypeScript的类型检查速度。

二、解决报错的方案

方案1:尾递归优化(TypeScript 4.5+)

TypeScript 4.5及以上版本支持尾递归优化,通过引入辅助参数累积当前处理的类型,让编译器可以优化递归深度:

export type NestedObjectValue<
  T extends Record<string, any>,
  K extends Readonly<string>,
  Current = T
> =
  K extends `${infer F extends string}.${infer R extends string}`
    ? NestedObjectValue<T, R, Current[F]>
    : K extends keyof Current
      ? Current[K]
      : never

这里将当前遍历到的类型存储在Current参数中,每次递归仅更新Current而非重新从T开始,编译器可识别这种尾递归并优化,避免深度超限。

方案2:迭代式类型替代递归

将路径拆分为数组片段,再通过类似迭代的方式遍历类型,相比深度递归更稳定:

// 先拆分路径为字符串数组
type SplitPath<S extends string> = S extends `${infer F}.${infer R}` ? [F, ...SplitPath<R>] : [S];

// 用Reduce逻辑遍历路径
type PathReduce<T, Path extends string[]> = Path extends [infer F extends keyof T, ...infer Rest extends string[]]
  ? PathReduce<T[F], Rest>
  : T;

export type NestedObjectValue<
  T extends Record<string, any>,
  K extends Readonly<string>
> = PathReduce<T, SplitPath<K>>;

这种实现将路径拆分与类型遍历逻辑分离,减少了递归的深度依赖,计算效率更高,也更不容易触发深度限制。

方案3:限制路径最大深度

如果业务中路径的最大深度可预见(比如最多10层),可以直接编写固定层数的条件类型,完全避免递归:

export type NestedObjectValue<
  T extends Record<string, any>,
  K extends Readonly<string>
> =
  // 可根据业务调整最大层数,超过则返回never或自定义处理
  K extends `${infer F1}.${infer F2}.${infer F3}.${infer F4}.${infer F5}.${infer Rest}`
    ? never
    : K extends `${infer F1}.${infer F2}.${infer F3}.${infer F4}`
      ? T[F1][F2][F3][F4]
      : K extends `${infer F1}.${infer F2}.${infer F3}`
        ? T[F1][F2][F3]
        : K extends `${infer F1}.${infer F2}`
          ? T[F1][F2]
          : K extends keyof T
            ? T[K]
            : never;

该方案性能最优,但灵活性差,仅适用于路径深度固定的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 13:27:46