0Pricing
TypeScript Academy · 课时

递归深度与限制

理解 TypeScript 的递归深度限制。

递归深度与限制 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 4 节课。

TypeScript 有递归限制

递归类型功能强大,但类型检查器会限制实例化它们的深度。达到该限制后,就会出现深度错误。

// Excessively deep recursive types can produce:
// "Type instantiation is excessively deep and possibly infinite."

深度错误的来源

如果条件类型在递归时没有明确且不断缩小的基准情况,就可能不断扩展,尤其是在与大型联合类型或元组构建结合时。

type Repeat<N extends number, Acc extends unknown[] = []> =
  Acc["length"] extends N ? Acc : Repeat<N, [...Acc, unknown]>;
// Large N can exceed the instantiation depth.
type Five = Repeat<5>;

安全的有界递归

保持递归浅且有界。构建小型元组没有问题;基准情况 Acc["length"] extends N 会及时停止递归。

type Tuple<N extends number, Acc extends unknown[] = []> =
  Acc["length"] extends N ? Acc : Tuple<N, [...Acc, unknown]>;
type Three = Tuple<3>; // [unknown, unknown, unknown]
const t: Three = [1, 2, 3];
console.log(t.length);

尾递归类型模式

TypeScript 会通过携带累加器来优化某些尾递归条件类型,使其能够比朴素的嵌套方式进行更深的递归。

type Reverse<T extends unknown[], Acc extends unknown[] = []> =
  T extends [infer Head, ...infer Tail]
    ? Reverse<Tail, [Head, ...Acc]>
    : Acc;
type R = Reverse<[1, 2, 3]>; // [3, 2, 1]
const r: R = [3, 2, 1];
console.log(r);

累加器为何有用

通过累加器向前传递结果,可以使递归处于尾部位置;相比深度嵌套的条件类型,TypeScript 能更高效地展开这种递归。

type Join<T extends string[], Acc extends string = ""> =
  T extends [infer H extends string, ...infer R extends string[]]
    ? Join<R, Acc extends "" ? H : Acc>
    : Acc;
type First = Join<["a", "b", "c"]>; // "a"
const f: First = "a";
console.log(f);

避免非尾递归

将递归调用包裹在另一个类型操作中会破坏尾部位置,并可能更早触及限制。请改为在累加器中携带状态。

// Non-tail (can be costly): builds nesting around the recursive call
// type Bad<T> = T extends [infer H, ...infer R] ? [H, ...Bad<R>] : [];
// Tail-friendly alternative uses an accumulator parameter.
console.log("prefer accumulators");

明确限制深度

添加深度计数器,使类型在固定层数后停止,以完整性换取确定的终止。

type Flatten<T, Depth extends unknown[] = []> =
  Depth["length"] extends 5
    ? T
    : T extends (infer U)[]
      ? Flatten<U, [...Depth, unknown]>
      : T;
type X = Flatten<number[][]>; // number
const x: X = 7;
console.log(x);

实际深度通常没有问题

日常使用的递归类型、链表、树和 JSON 几乎不会触及该限制,因为实例化的数据很浅。限制主要影响繁重的类型级计算。

type Json = string | number | boolean | null | Json[] | { [k: string]: Json };
const v: Json = { a: { b: { c: 1 } } }; // fine, shallow
console.log(v);

识别错误消息

当您看到“深度过深且可能是无限的”时,请检查是否缺少基准情况、累加器处于非尾部位置,或输入过大。

// Fixes: add/tighten the base case, switch to tail recursion,
// reduce input size, or cap with a depth counter.
console.log("check base case and tail position");

运行时递归是另一回事

这些限制针对的是类型级递归。基于递归类型的普通递归函数会在运行时执行,其限制因素只有调用栈。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
function count<T>(n: TreeNode<T>): number {
  return 1 + n.children.reduce((a, c) => a + count(c), 0);
}
console.log(count({ value: 1, children: [{ value: 2, children: [] }] }));

在限制范围内设计

优先使用浅层数据、带累加器的尾递归模式以及明确的深度上限。养成这些习惯后,您在实际代码中很少会遇到该限制。

type Length<T extends unknown[]> = T["length"];
type N = Length<[1, 2, 3]>; // 3
const n: N = 3;
console.log(n);

快速检查:递归限制

请测试您对递归深度和限制的理解。

回顾:递归深度和限制

您了解到 TypeScript 会限制类型级递归的深度,带累加器的尾递归模式和明确的深度上限会有所帮助,而日常使用的递归数据很少触及该限制。

type Reverse<T extends unknown[], Acc extends unknown[] = []> =
  T extends [infer H, ...infer R] ? Reverse<R, [H, ...Acc]> : Acc;
const r: Reverse<[1, 2]> = [2, 1];
console.log(r);

常见问题解答

「递归深度与限制」课时是免费的吗?

是的 — 「递归深度与限制」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 4 节课。

「递归深度与限制」这节课中我会学到什么?

理解 TypeScript 的递归深度限制。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 TypeScript Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「递归深度与限制」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 TypeScript Academy 课中编写并运行代码吗?

能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 递归类型定义
  2. 树形结构的类型标注
  3. JSON 值类型
  4. 递归深度与限制
← 返回 TypeScript Academy