What a 10x Faster Typechecker Actually Changes

The first time I ran the native TypeScript compiler on our largest package, I assumed it had crashed. The command returned before I'd finished tabbing back to the terminal. A full typecheck that used to take 47 seconds came back in a little under five. I ran it again to be sure I hadn't cached my way into a lie.
The headline number — roughly 10x — is easy to repeat and easy to underrate. Everyone frames it as "typecheck is fast now," which is true and boring. The interesting part is what a fast typechecker lets you stop avoiding. Speed at that magnitude doesn't just shorten a step in your pipeline. It changes which decisions were even worth making.
The clock was never the real cost
When a check takes 45 seconds, you don't run it 45 seconds' worth less often. You run it a completely different way. You batch. You push a branch and let CI find the type error twenty minutes later instead of catching it locally. You alt-tab and lose the thread. The cost of a slow check isn't the seconds — it's the workflow you build to avoid paying them.
Our team had accumulated a whole shadow set of habits around this. We ran tsc in a separate terminal with --watch and mostly ignored it. We split the monorepo's typecheck across CI shards to keep any single job under three minutes. We had a lint rule that banned certain generic patterns, ostensibly for readability, but really because they made the checker crawl. None of these were decisions anyone wrote down. They were sediment.
A near-instant checker dissolves that sediment. When the feedback loop is under five seconds, you run the real check on every save, and the elaborate --watch dance stops earning its keep. That's the first-order win. The second-order wins are where it gets genuinely interesting.
You stop routing around your own type system
Here's the pattern I didn't expect. For years I'd been quietly avoiding expensive types. Not consciously — it showed up as small, defensible-looking choices. A discriminated union with fifteen members that I flattened into a looser shape "for simplicity." A recursive conditional type I rewrote as a manual overload list because inference on the recursive version added a noticeable pause to every check. An as cast where a proper mapped type would've been correct but slow to resolve.
Each of these was a micro-surrender. Individually rational, collectively a tax on correctness. I was trading type safety for compiler throughput without admitting that's what I was doing.
Consider the kind of type I'd been avoiding:
// The version I actually shipped — loose, but the checker liked it
type EventHandler = (event: { type: string; payload: unknown }) => void;
// The version I wanted — precise, but it used to make every
// check that touched this file noticeably slower
type EventMap = {
"ticket.created": { ticketId: string; assigneeId: string | null };
"ticket.merged": { sourceId: string; targetId: string };
"comment.added": { ticketId: string; body: string; internal: boolean };
};
type EventHandler = <K extends keyof EventMap>(event: {
type: K;
payload: EventMap[K];
}) => void;
The second version is strictly better. It catches the wrong payload shape at the call site instead of at runtime. The only reason I'd reach for the first was that, multiplied across a file imported everywhere, the precise version cost real time on every check. When that cost drops by an order of magnitude, the calculus flips. The expensive-but-correct type is just the correct type now.

CI gating gets to be honest again
The other place this lands is CI. When typecheck is slow, it drifts to the back of the pipeline — you run the cheap stuff first (lint, format) and gate the expensive typecheck behind it, or you parallelize it into its own long-running job that everyone learns to wait on. Either way, type errors surface late, and "late" in CI means after you've context-switched to something else.
A fast checker earns a spot at the front. We moved typecheck to run before the test suite, because it now finishes faster than the tests spin up their containers. That reordering matters more than it sounds. A type error is a cheaper signal than a failing test — it points at exactly one place and never flakes. Surfacing it first means the majority of "red build" cases resolve in seconds, not minutes, and the developer is still holding the mental model that produced the error.
There's a subtler shift too. When typecheck was expensive, we treated it as a coarse gate — pass or fail on the whole package. Now that running it is nearly free, we run it per-changed-package on every commit in a pre-push hook, not just in CI. The gate moved left. Errors that used to cost a full CI round-trip to discover now cost a few seconds locally. That's not a speed improvement so much as a category change in where mistakes get caught.
The trap: fast doesn't mean free to ignore
I want to be careful not to oversell this. A faster checker does not make your types better. It removes a specific excuse for making them worse, which is not the same thing. If your codebase leans on any and casts, the native compiler will validate that mess just as fast as it validates good types. Speed is neutral about correctness.
The failure mode I'm now watching for on my own team is the opposite of the old one. When checks were slow, people under-typed to keep the compiler happy. When checks are instant, the temptation is to stop paying attention to type-level cost entirely — to write a genuinely pathological type, notice nothing because five seconds is invisible, and only feel it later when it compounds with a thousand others. Fast feedback hides gradual regressions better than slow feedback does. The 45-second check was an unpleasant early-warning system. Losing it means we have to be a little more deliberate, not less.
So the discipline shifts. Before, the question was "can I afford this type." Now it's "is this type actually earning its precision," which is a better question to be asking anyway.
The takeaway
The 10x number sells the tool, but it undersells the change. A near-instant typechecker doesn't just make an existing step faster — it retires the workarounds you built when it was slow, and it quietly returns a set of type-level choices you'd been making for performance reasons back to being choices about correctness.
Measure the second-order effects, not the stopwatch. If all you got from the native compiler is a faster CI job, you left most of the value on the table. The real win is writing the type you meant to write in the first place — and finally having no reason not to.