Performance & Type Checker Optimization
⏱️ Performance & Type Checker Optimization
tsc itself. A project can compile to perfectly fast JavaScript while taking 45 seconds to type-check, or make an editor's language server freeze on every keystroke — and the very techniques this course just spent seven chapters teaching are exactly what can cause it.
Why Type Inference Gets Expensive
Every advanced technique in this course has a cost proportional to how much work the compiler does to resolve it — and that cost compounds:
Union/Intersection Explosion
Chapter 6's template literal types distribute over unions — a template applied to two unions of 20 members each can produce a union of 400 combinations, all of which the checker must track.
Recursion Depth
Chapters 1 and 2's recursive conditional types are re-evaluated at every use site — deep, frequently-used recursive types multiply that cost across the whole project.
Generic Instantiation
Every call to a generic function creates a fresh instantiation for the checker to resolve — Chapter 3's Curried<Args, Return> is cheap once, expensive called thousands of times across a codebase.
Structural Comparison
Checking whether one object type is assignable to another compares every property — the bigger and more deeply nested the shape, the more comparisons per check.
🚧 Common Performance Cliffs
A Real Cliff: Combinatorial Template Literals
Explodes Combinatorially
Bounded
Deeply Nested Conditionals
A conditional type chain ten levels deep, used across hundreds of files, means ten evaluations at every single use — not once.
Large Literal Unions From as const
A mapped type iterating over a 500-entry as const array's keys is doing real, non-trivial work five hundred times.
🏗️ Incremental Builds
The most effective performance fix often has nothing to do with simplifying any individual type — it's making the compiler avoid re-checking work it already did:
Incremental Compilation & Project References
.tsbuildinfo
A cache file recording what was already type-checked — tsc --incremental skips unchanged files entirely on the next run.
Project References
Splits a large codebase into independently-checked units — the CI equivalent of this idea is exactly why Course 3's tsc --noEmit pipeline step matters: catching type errors fast, before the slower steps run.
Diagnosing Slowness Instead of Guessing
TypeScript ships its own profiling tools — the same "measure, don't guess" discipline from Course 3's runtime performance chapter applies to the type checker too:
💻 Coding Challenges
Challenge 1: Identify the Cliff
Given a type type AllCombos<A extends string, B extends string, C extends string> = \`${A}-${B}-${C}\` applied to three 15-member unions, explain (in a comment, no code needed) roughly how many total string literal members the resulting type has, and why that's a performance concern if reused across many files.
Goal: Practice recognizing combinatorial growth before it becomes a real project's slow build.
Challenge 2: Set Up an Incremental Build
Write a tsconfig.json with "incremental": true and a custom tsBuildInfoFile path, then explain in a comment what changes about a second tsc run compared to the first.
Goal: Practice the configuration that gives the single biggest, easiest performance win for most projects.
Challenge 3: Simplify a Recursive Type
Take Chapter 2's unbounded Repeat<T> (the one that causes "excessively deep and possibly infinite") and rewrite it as the depth-limited version, then explain why the depth-limited version is cheaper for the type checker, not just "more correct."
Goal: Practice connecting a correctness fix from Chapter 2 to its performance benefit, not just its "it no longer errors" benefit.
It's tempting to preemptively simplify every clever type this course covered "just in case" it's slow — that's the type-checker equivalent of Course 3's runtime optimization gotcha. Most projects never come close to a real performance cliff; the ones that do usually have one or two identifiable offenders (a giant template-literal union, a recursive type on a hot path) findable with --extendedDiagnostics, not a vague sense that "advanced types are slow." Measure first, simplify the specific thing the trace points at.
🎯 What's Next
Every technique from this course — conditionals, recursion, tuples, guards, brands, type-level programming, augmentation, and now performance awareness — comes together in the final chapter: Advanced Patterns in Practice, combining them into a small, genuinely type-safe framework.