Recursion Depth and Limits
Understand TypeScript's recursion depth constraints.
Recursion Depth and Limits is a free TypeScript Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the TypeScript Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
TypeScript Has Recursion Limits
Recursive types are powerful, but the type checker limits how deeply it will instantiate them. Hit the limit and you get a depth error.
// Excessively deep recursive types can produce:
// "Type instantiation is excessively deep and possibly infinite."Where Depth Errors Come From
Conditional types that recurse without a clear, shrinking base case can spiral, especially when combined with large unions or tuple building.
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>;A Safe Bounded Recursion
Keep recursion shallow and bounded. Building a small tuple is fine; the base case Acc["length"] extends N stops it promptly.
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);Tail-Recursive Type Patterns
TypeScript optimizes certain tail-recursive conditional types by carrying an accumulator, allowing deeper recursion than naive nesting.
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);Why Accumulators Help
Passing results forward in an accumulator keeps the recursion in tail position, which TypeScript can unroll more efficiently than deeply nested conditionals.
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);Avoiding Non-Tail Recursion
Wrapping the recursive call inside another type operation breaks tail position and can hit limits sooner. Prefer carrying state in an accumulator instead.
// 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");Capping Depth Explicitly
Add a depth counter so the type bails out after a fixed number of levels, trading completeness for guaranteed termination.
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);Practical Depth Is Usually Fine
Everyday recursive types, linked lists, trees, JSON, never hit the limit because the data you instantiate is shallow. Limits mainly affect heavy type-level computation.
type Json = string | number | boolean | null | Json[] | { [k: string]: Json };
const v: Json = { a: { b: { c: 1 } } }; // fine, shallow
console.log(v);Recognizing the Error Message
When you see "excessively deep and possibly infinite", look for a missing base case, an accumulator in non-tail position, or an input that is too large.
// 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");Runtime Recursion Is Separate
These limits are about type-level recursion. Ordinary recursive functions over recursive types run at runtime and are bounded only by the call stack.
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: [] }] }));Designing Within the Limits
Favor shallow data, tail-recursive patterns with accumulators, and explicit depth caps. With these habits you rarely meet the limit in real code.
type Length<T extends unknown[]> = T["length"];
type N = Length<[1, 2, 3]>; // 3
const n: N = 3;
console.log(n);Quick Check: Recursion Limits
Test your understanding of recursion depth and limits.
Recap: Recursion Depth and Limits
You learned that TypeScript caps type-level recursion depth, that tail-recursive patterns with accumulators and explicit depth caps help, and that everyday recursive data rarely hits the limit.
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);Frequently asked questions
Is the “Recursion Depth and Limits” lesson free?
Yes — the full text of “Recursion Depth and Limits” is free to read here on the web, and the TypeScript Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the TypeScript Academy course, upgrade to CoddyKit PRO.
What will I learn in “Recursion Depth and Limits”?
Understand TypeScript's recursion depth constraints. You practise TypeScript Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start TypeScript Academy?
No prior experience is required. TypeScript Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Recursion Depth and Limits” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this TypeScript Academy lesson?
Yes. Every TypeScript Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Recursive Type Definitions
- Typing Tree Structures
- JSON Value Types
- Recursion Depth and Limits