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

如何在链式类型构建场景下提升TypeScript性能?

TypeScript链式泛型类型的性能优化方案

你提供的代码实现了一个逐步构建的链式泛型结构,但多层链式调用会触发TypeScript的Type instantiation is excessively deep and possibly infinite.(2589)错误。以下是不牺牲功能前提下的性能优化措施:

原代码

type LMerge<T1, T2> = {
  [k in keyof T1]: k extends keyof T2 ? T2[k] : T1[k]
}

type PossibleGenerics = {
  Output?: unknown
  Error?: unknown
  ResultResolverController?: unknown
  ErrorResolverController?: unknown
}

type Generics = Required<PossibleGenerics>

type NodeItem<T extends Generics, Defaults extends Generics> = {
  <
    S extends PossibleGenerics = {},
    NewDefaults extends PossibleGenerics = {},
    UpdatedDefaults extends Generics = LMerge<Defaults, NewDefaults>,
    Child extends Generics = LMerge<UpdatedDefaults, S>,
  >(): NodeItem<Child, UpdatedDefaults>
  NodeItem: T
}

const example = {} as NodeItem<
  {
    Output: string
    Error: string
    ResultResolverController: string
    ErrorResolverController: string
  },
  Generics
>
const exampleUsage1 = example<{ Output: number }, { Error: Error }>()
const exampleUsage2 = exampleUsage1<{ Output: number }>()
// ...
const exampleUsage21 = exampleUsage20<{ Output: number }>()  // Type instantiation is excessively deep and possibly infinite.(2589)

优化措施

1. 移除冗余的中间泛型参数

原代码中UpdatedDefaults和Child是推导而来的中间泛型,TypeScript每次处理都会重复计算这些类型,增加检查开销。直接在返回类型中合并计算,减少泛型参数数量:

// 更高效的合并实现:先移除T1中与T2重叠的键,再合并T2
type LMerge<T1, T2> = Omit<T1, keyof T2> & T2;

type NodeItem<T extends Generics, Defaults extends Generics> = {
  <S extends PossibleGenerics = {}, NewDefaults extends PossibleGenerics = {}>(): NodeItem<
    LMerge<LMerge<Defaults, NewDefaults>, S>,
    LMerge<Defaults, NewDefaults>
  >;
  NodeItem: T;
};

2. 替换动态映射合并为显式键合并

针对Generics的固定结构,直接显式合并已知键,避免遍历所有键的性能损耗:

type LMerge<T extends Generics, U extends PossibleGenerics> = {
  Output: U['Output'] extends undefined ? T['Output'] : U['Output'];
  Error: U['Error'] extends undefined ? T['Error'] : U['Error'];
  ResultResolverController: U['ResultResolverController'] extends undefined ? T['ResultResolverController'] : U['ResultResolverController'];
  ErrorResolverController: U['ErrorResolverController'] extends undefined ? T['ErrorResolverController'] : U['ErrorResolverController'];
};

这种方式跳过了动态映射类型的遍历逻辑,TypeScript处理起来更高效。

3. 添加递归深度限制

引入可选的Depth泛型参数,限制递归实例化的最大深度,避免触发过深错误:

type NodeItem<T extends Generics, Defaults extends Generics, Depth extends number = 20> = {
  <S extends PossibleGenerics = {}, NewDefaults extends PossibleGenerics = {}>(): 
    Depth extends 0 ? NodeItem<T, Defaults> : // 深度耗尽时保持当前类型
    NodeItem<
      LMerge<LMerge<Defaults, NewDefaults>, S>,
      LMerge<Defaults, NewDefaults>,
      // 通过预定义数组实现深度递减
      [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19][Depth]
    >;
  NodeItem: T;
};

默认设置深度为20,足以覆盖绝大多数使用场景,同时避免无限递归。

4. 延迟类型计算到属性访问时

将类型合并逻辑从函数返回类型移到NodeItem属性上,仅在访问该属性时才触发类型计算,减少即时推导负担:

type NodeItem<T extends Generics, Defaults extends Generics> = {
  <S extends PossibleGenerics = {}, NewDefaults extends PossibleGenerics = {}>(): NodeItem<any, LMerge<Defaults, NewDefaults>>;
  // 延迟计算最终类型,仅在访问时解析
  get NodeItem(): LMerge<Defaults, T>;
};

函数返回时用any占位,直到访问NodeItem属性才合并出最终类型,大幅降低递归类型的即时计算开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 00:15:38