BeCuriouzLearn something new every day
Why Your Types Feel Slow — and How to Speed Them Up
💡 Type-checking is real computation — recursive conditionals and union-combining template literals can blow up combinatorially, so name types, annotate returns, and cap recursion to keep the compiler fast.
You've built rich generic types across this path. At scale, the type-checker itself can become the bottleneck: a red squiggle takes seconds to appear, tsc crawls, and your editor lags. The cause is that the compiler eagerly evaluates type-level work, and some patterns explode exponentially. The fix isn't to abandon types — it's to know which shapes are cheap.
The biggest offenders are deeply recursive conditional and mapped types and large unions. A conditional like T extends U ? X : Y is re-checked for every member of a union, so distributing over a 50-member union inside a recursive type can mean thousands of instantiations. Template literal types that combine unions are the classic trap: ${A}-${B} produces A.length × B.length string literals — two 100-member unions become 10,000 types the compiler must materialize.
// 🐢 explodes: every union member re-instantiates the conditional
type DeepFlatten<T> = T extends readonly (infer U)[] ? DeepFlatten<U> : T;
// 🐇 cheaper: cap recursion, prefer interfaces over big intersections
interface Point { x: number; y: number } // named, cached
type Merge = Point & { z: number }; // intersection recomputed each usePractical speed-ups: name your types with interface (they're cached and referenced by name, not re-expanded), add explicit return-type annotations to functions so the compiler doesn't infer sprawling types, prefer interface extends over deep & intersections, and set "incremental": true plus project references to avoid re-checking unchanged code. When a type is genuinely pathological, use --extendedDiagnostics and --generateTrace to find the hot spot instead of guessing.