I spent $4,000 to make TypeScript faster
Based on Theo - t3․gg's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
TypeScript 5.9 beta includes a language-server performance fix rooted in TypeScript’s type instantiation caching behavior, not TRPC-specific logic.
Briefing
TypeScript’s language server performance is getting a major boost after a deep audit traced slowdowns to a specific caching failure inside TypeScript’s type instantiation logic—work that’s now landed in the TypeScript 5.9 beta. The practical payoff is faster type checking and fewer “restart the TypeScript server” moments in large full-stack TypeScript codebases that lean on heavy inference patterns, including TRPC and Zod.
The catalyst was frustration with TypeScript language server (LSP) responsiveness rather than runtime performance. In large projects, developers repeatedly restart the TypeScript server because it falls behind, crashes, or can’t keep up with inference-heavy type computation. The investigation began with an audit of TRPC—built to connect backend and frontend with end-to-end type safety—because TRPC’s router structure forces substantial inference across nested routers, procedures, inputs, and outputs. Even without explicit type annotations, TRPC’s inferred types propagate through the stack, so any change can trigger broad recomputation.
A key expectation was that TypeScript project references would isolate work between router modules, so changes in one area wouldn’t force rechecking everywhere. That didn’t happen as hoped. The audit found only small TRPC-specific issues, but uncovered “weird cache behaviors” in TypeScript itself when dealing with long chains of nested type operations. The failure mode showed up in merge-like generic types: at certain nesting depths, type checking became both slow and incorrect, with the type checker rerunning work that should have been cached.
The fix centered on how TypeScript builds cache keys for type instantiation—specifically, that cache lookup depended on instantiation calls in a way that forced expensive checks even when results might already be cached. In longer chains, the problem compounded. TypeScript contributors introduced a new active mapper cache path so the checker can reuse cached results more reliably, trading some memory for dramatically fewer instantiations.
Benchmarks using “at-test” (a TypeScript self-benchmark that counts type instantiation steps in a type-safe way) showed instantiation counts ramping extremely fast as merge chains grew. In TRPC-based repositories, TypeScript 5.8 hit roughly 1.2–1.3 million instantiations, while the updated branch cut that to about 817k—nearly halving check time with a modest memory increase. The change also enabled type checking of deeper chains (around 50 items) that previously only worked up to roughly the 15–25 range before becoming painfully slow.
Beyond performance, TypeScript 5.9 beta adds editor-focused improvements. The d--init tool for generating tsconfig files is simplified, import defer supports lazy module execution, and DOM API hover descriptions now include short summaries instead of only MDN links. The most visible quality-of-life upgrade is “expandable hovers,” which lets developers see the full inferred type instead of unhelpful hover placeholders like “options: options.” There’s also configurable maximum hover length to reduce truncation pain, plus guidance that VS Code support may lag behind (working in Insiders/beta first).
The release also includes smaller compiler and language-server wins, including avoiding unnecessary closure creation in certain pass-through function patterns (reported up to ~11% faster type checks in some cases). Looking ahead, the native TypeScript rewrite in Go continues via nightly “TypeScript native previews,” while 5.9 remains in active development. The overall message: TypeScript’s language server is becoming more usable for inference-heavy apps, and the improvements are landing in ways that directly reduce developer friction—not just theoretical benchmarks.
Cornell Notes
TypeScript 5.9 beta brings a substantial language-server performance fix tied to how TypeScript caches type instantiations. The slowdown—especially painful in large inference-heavy projects like TRPC and Zod—came from cache-key behavior that forced expensive type checks even when cached results should have been reused, and the issue worsened in long nested type chains. Contributors added an “active mapper” caching approach that reduced instantiation counts and cut type-check time roughly in half in TRPC-based repositories, while enabling deeper (≈50-item) type chains that previously degraded around 15–25. Alongside performance, the release improves editor ergonomics with expandable hovers, better DOM API hover summaries, and configurable hover truncation. These changes matter because they directly reduce editor lag and the need to restart the TypeScript server in real-world codebases.
Why did TRPC-style full-stack type safety create language-server pain in large codebases?
What was the key technical failure inside TypeScript that made caching unreliable?
How did the “active mapper” caching change improve performance?
How were instantiation counts measured, and why does that matter?
What editor improvements in TypeScript 5.9 beta target day-to-day developer usability?
What smaller optimization was mentioned beyond caching and editor hover features?
Review Questions
- What specific caching mechanism change reduced type instantiation counts, and why did the previous approach fail more severely in deep type chains?
- How does TRPC’s inference model make TypeScript recomputation more expensive when code changes, even without explicit type annotations?
- Which TypeScript 5.9 beta editor features directly improve how developers inspect inferred types, and what problem do they solve?
Key Points
- 1
TypeScript 5.9 beta includes a language-server performance fix rooted in TypeScript’s type instantiation caching behavior, not TRPC-specific logic.
- 2
Long chains of nested type operations (e.g., merge-like generics) previously caused cache lookups to trigger expensive instantiation work even when cached results should have been reused.
- 3
An “active mapper” caching approach reduced instantiation counts substantially (nearly halving check time in TRPC-based repositories) while trading some memory.
- 4
The change enables deeper type chains (around 50 items) that previously became slow or incorrect beyond roughly the 15–25 range.
- 5
Expandable hovers in TypeScript 5.9 beta make inferred types readable in-editor instead of showing placeholder hover text like “options: options.”
- 6
DOM API hover summaries now provide quick descriptions rather than only linking to MDN, improving immediate comprehension.
- 7
Smaller compiler/language-server optimizations include avoiding unnecessary closure creation in certain pass-through patterns, with reported gains up to ~11% in some cases.