A Go compiler for the language I write every day
On July 8th, TypeScript 7.0 shipped, and the headline is that the compiler isn't written in TypeScript anymore. It's written in Go.
That sounds like inside baseball. A language's compiler getting ported from one language to another is the kind of thing that gets applause at a conference and changes nothing about your Tuesday. This one is different, and the reason it's different is the same reason I noticed it at all: I run tsc on this exact codebase, and I've watched the number go down.
This site is a Next.js app with Payload on top of it. Every save runs a type-check somewhere, in the editor, in the pre-commit path, in the build. TypeScript isn't a tool I reach for occasionally. It's the thing sitting between me and every change I make. So when the team that maintains it says the whole thing is now 8 to 12 times faster, that's not a benchmark to me. That's my feedback loop.
What actually shipped
The short version: Microsoft rewrote the TypeScript compiler as a native Go program, and 7.0 is the first stable release of it. The old JavaScript-based compiler didn't disappear. It lives on as the 6.x line. But 7.0 is where the native port becomes the default tsc you install with the normal typescript package. (I first saw this in InfoQ's writeup, but the figures below are Microsoft's own, from their announcement post.)
The numbers they published are large enough to be suspicious of, so here they are with names attached:
- VS Code's own build: 125.7s to 10.6s (11.9x)
- Sentry: 139.8s to 15.7s (8.9x)
- Bluesky: 24.3s to 2.8s (8.7x)
- Playwright: 12.8s to 1.47s (8.7x)
Memory dropped about 18% on the VS Code codebase. The editor case is the one I keep coming back to: opening a file with type errors in VS Code went from 17.5 seconds to under 1.3. That's the difference between "I'll go get coffee" and not noticing it happened.
These aren't projected numbers on a toy repo. They're real projects the team measured, and the spread is tight enough, everything landing in that 8x-to-12x band, that I believe the shape of it even if any single figure was picked to look good.
The part that makes it matter
Here's what people miss when they see a speed number: fast type-checking already existed. esbuild strips types in milliseconds. So does swc. Biome is in the same neighborhood. If all I wanted was to turn .ts into .js quickly, that was solved years ago.
The catch is that those tools don't type-check. They throw the types away. They read your annotations, delete them, and hand you JavaScript without ever checking whether any of it was true. That's a legitimate thing to do in a build pipeline, since the build doesn't need to know your types are correct, it just needs the JavaScript. But it means you're running two tools with two different mental models: a fast one that transpiles, and a slow one (tsc) that actually verifies.
TypeScript 7.0 closes the speed gap while keeping the checking. It's the real checker, doing the real work, at a speed that used to require giving the checking up. That's the part that made me pay attention. The tradeoff I'd quietly accepted, thorough or fast, pick one, isn't the tradeoff anymore.
Why Go, and what you get to tune
The rewrite is in Go, and the reason that matters in practice is threads. The old compiler was single-threaded JavaScript. The new one spreads type-checking and building across cores, and it exposes that as knobs you can turn: --checkers for how many threads do type-checking, --builders for the build side, and --singleThreaded for when you're running somewhere constrained and want the old behavior back.
I don't expect to touch those flags most days. Defaults are defaults for a reason. But the fact that they exist tells you the architecture is genuinely parallel now, and on a big monorepo, being able to tell the compiler "use eight checkers, this machine can take it" is a lever that didn't exist before.
The editor side is built on the Language Server Protocol, which is the quiet win here. It's why the file-open number dropped so hard. Most of my time with TypeScript isn't spent running a build. It's spent typing and waiting for the red squiggle to catch up to me. Making that instant is worth more to my day than shaving a build I run a few times an hour.
The migration is real, and 7.0 has a gap
None of this is free. TypeScript 7.0 turns 6.0's deprecations into hard errors, and it makes strict and esnext the defaults. That's the right direction, but it means "upgrade" isn't always "bump the version number and move on."
The escape hatch is a compatibility package, @typescript/typescript6, which ships a tsc6 binary and re-exports the old API. If something in your toolchain depends on the JavaScript compiler's internals, you can keep it running on that while you sort out the native one.
That "depends on the internals" case is bigger than it sounds, because the stable programmatic API isn't in 7.0. It's slated for 7.1. A large part of the ecosystem reaches into the compiler's guts rather than just running it, and Microsoft names the ones that are blocked on it: typescript-eslint, plus the framework tooling for Vue, Svelte, Astro, MDX, and Angular. Until 7.1 ships that API, a lot of those tools lean on the 6.x line even while your tsc is native. So the honest picture on day one is that the compiler you run got fast, and the tools built on top of it are still catching up.
For a project like this one, that's fine. I run tsc and I run eslint, and I can afford to let the eslint side trail. For a team whose build is a tall stack of compiler plugins, "TypeScript 7 is out" and "we can adopt TypeScript 7" are going to be months apart.
So I ran it on this repo
I didn't want to take the numbers on faith, so I pointed the native compiler at this codebase. It's small: 76 TypeScript files plus the generated Next types, nothing like the size of the projects Microsoft measured. On a 12-core machine, timing a full type-check that finds no errors, median of five runs:
tsc5.9.3, full check: about 2.0 seconds- the native compiler, full check: about 0.29 seconds
That's roughly 7x, a little under Microsoft's 8-to-12x range, which is what I'd expect on a project this small. When there's less code to check, the fixed startup cost is a bigger share of the total, so the ratio comes down. Both compilers agreed there were zero type errors, so it's the same work, just faster.
The number that actually surprised me was the incremental one. tsc's warm rebuild, the fast path where it reuses its cache and only rechecks what changed, took about 1.4 seconds. The native compiler doing a full check from cold beat that by roughly 5x. The thing I reach for when I want tsc to be quick was slower than the new compiler not even trying to be clever.
And the threading isn't just a spec-sheet line. Forcing --singleThreaded roughly doubled the time, from 0.29s to 0.64s, on 76 files. On a codebase with real size behind it, that gap widens.
One caveat, since the whole point of this section is not taking numbers on faith: what I ran was the native preview build (the tsgo binary), not a released typescript@7 install. It's the same Go engine, and stable 7.0 ships it as your regular tsc, but I want to be precise about what produced the numbers above.
What I'm actually going to do about it
Nothing dramatic, and I think that's the point. I'll keep an eye on when typescript@7 is a clean drop-in for this project, leave typescript-eslint where it is until the 7.1 API gives it a native path, and enjoy the editor feeling faster, which is the improvement I'll actually notice every day even though it got the least attention in the announcement.
The compiler being written in Go is a good headline. But the thing worth caring about isn't the language it's written in. It's that the choice between checking everything and checking it fast quietly stopped being a choice. That's rare. Most performance work makes a thing you already do a little quicker. This removes a compromise I'd stopped noticing I was making.
A few things I'm taking away from it:
- Speed with the checking left intact is the whole story. Fast transpilers were never the hard part. A fast type-checker is.
- The editor number matters most. Builds run a few times an hour; the type-checker in your editor runs every time you stop typing.
- "Released" and "adoptable" aren't the same date. The 7.1 programmatic API is the gate for the plugin ecosystem, and that's what most real toolchains are waiting on, not the compiler itself.
Comments
No comments yet. Be the first to comment.