Challenge 3: Simplify a Recursive Type — Possible Solution ==================================================================== // BEFORE — Chapter 2's unbounded version: type Repeat = [T, ...Repeat]; // ❌ "Type instantiation is excessively deep and possibly infinite" — // there is no base case, so the compiler has no way to ever stop. // AFTER — the depth-limited version from Chapter 2: type RepeatLimited = Acc["length"] extends N ? Acc : RepeatLimited; type ThreeStrings = RepeatLimited; // [string, string, string] WHY THE DEPTH-LIMITED VERSION IS CHEAPER FOR THE TYPE CHECKER -------------------------------------------------------------- This is about more than just "it doesn't error" — it's genuinely cheaper to evaluate, for a specific reason: 1. THE UNBOUNDED VERSION HAS NO TERMINATION CONDITION AT ALL. Every time TypeScript tries to resolve `Repeat`, it immediately needs to resolve `Repeat` again to know what comes after the first element — which needs to resolve `Repeat` again, and so on. The compiler has to detect this runaway recursion and bail out once it hits its internal depth limit (currently around 50 levels for most recursive type operations) — but reaching that limit and giving up is itself wasted compiler work spent recursing 50 levels deep before concluding "this will never finish." 2. THE DEPTH-LIMITED VERSION HAS A CONCRETE, CHEAP-TO-CHECK STOPPING CONDITION: `Acc["length"] extends N`. Each recursive step does a small, fixed amount of work (append one element, compare a tuple length to a number literal) and then either stops or takes exactly one more step — the total work is proportional to N, not unbounded. 3. Critically, N is chosen by the CALLER and is typically small (3, 5, 10) for any realistic use — so `RepeatLimited` does a small, bounded, predictable amount of work every time it's used, versus the unbounded version doing an ever-increasing (and ultimately futile) amount of work until the compiler's safety limit kicks in. 4. This generalizes beyond this one example: ANY recursive type without an explicit, reachable base case forces the compiler to either detect non-termination (wasted work) or, worse, actually recurse to its maximum depth on every single use site across a project — while a properly bounded recursive type's cost is small and predictable, which is exactly why "add a base case / depth limit" is both a correctness fix (Chapter 2) and a performance fix (this chapter) for the same root problem.