TypeScript's Native Port and the End of JavaScript Running JavaScript
Source: typescript
Looking back at Microsoft’s March 2025 announcement about porting the TypeScript compiler to a native binary, the most interesting part is not the headline number. Ten times faster is compelling, but the engineering decisions underneath it are worth unpacking.
The TypeScript compiler has always been written in TypeScript, compiled to JavaScript, and run on Node.js. That was a reasonable choice in 2012. TypeScript needed to bootstrap, the team already knew the language, and it served as a proof-of-concept for the language itself. For a long time, Node.js was fast enough. The V8 JIT is genuinely impressive, and the TypeScript team spent years optimizing the compiler within the constraints of a garbage-collected, single-threaded runtime.
But codebases kept growing. By the time large monorepos started routinely exceeding a million lines of TypeScript, build times began to matter in ways that no amount of incremental compilation could fully paper over. The VS Code codebase itself became a canonical benchmark: full type-checking taking over a minute. That is the kind of number that makes a native port worth the engineering cost.
Why Go
The choice of Go over Rust is worth dwelling on, because essentially every other native JavaScript tooling project in recent years has gone the Rust route. esbuild is the main exception, and it is not a coincidence that esbuild is also the tool that first demonstrated to the JavaScript ecosystem what native speeds actually felt like.
Biome, Oxc, SWC, and Rspack are all Rust. There is a good reason for that: Rust gives you precise memory control with no garbage collector, which matters when you are parsing and transforming millions of lines of code and want predictable latency without GC pauses.
But the TypeScript compiler is not a simple transformer. It maintains rich, interconnected data structures across the entire type-checking process. The borrow checker, while enforcing correct programs, would have made a mechanical port from TypeScript’s existing architecture extremely difficult. TypeScript’s internal representation involves a lot of shared mutable references and complex graph structures that work naturally under a garbage collector but would require significant redesign under Rust’s ownership model.
Go solves this differently. It has a garbage collector, so shared references just work. It has goroutines, which are cheap enough that you can parallelize type-checking across files without the overhead of OS threads. And critically, it is statically typed and compiles to native code, which eliminates the V8 JIT warm-up and the JavaScript runtime overhead entirely.
The microsoft/typescript-go project describes itself as a port, not a rewrite. The team translated the existing TypeScript compiler source largely mechanically, preserving the architecture and the algorithms. This is a different strategy from what the Biome team did when replacing ESLint and Prettier, or what the Oxc team did with their parser. Those projects redesigned from scratch with performance as a first-class constraint from day one. The TypeScript team chose correctness and compatibility as the primary constraints, with performance as the benefit of the runtime change alone.
What the 10x Number Means in Practice
The 10x figure comes from end-to-end compiler benchmarks, and the VS Code codebase is the headline case: build times dropping from roughly 77 seconds to around 7.5 seconds. That is a full type-check of a very large real-world codebase, not a synthetic benchmark.
For smaller codebases, the ratio may be somewhat lower because the fixed costs of startup and I/O dominate more. But even at 5x, the editor experience improvements are significant. The TypeScript language server, tsserver, is included in the port. That means every hover, every completion, every diagnostic in VS Code and Neovim and any other editor using the Language Server Protocol gets faster. The difference between a 200ms and a 40ms response to a “go to definition” request is not abstract; it is felt in the daily rhythm of working in a codebase.
Parallelism contributes substantially to the gain beyond raw native execution. The JavaScript runtime is single-threaded. TypeScript has had some parallel compilation support via workers, but it is limited and adds coordination overhead. Go’s goroutines make it straightforward to type-check independent modules concurrently, and the compiler can fully exploit multi-core machines without the friction that JavaScript’s concurrency model introduces.
The Ecosystem Implications
Full semantic compatibility is the stated goal: same type errors, same error messages, same resolution semantics. If the port ships correctly, the vast majority of TypeScript users will notice only the speed improvement.
The harder question is the plugin and API surface. The TypeScript compiler API has been used by an enormous number of tools: ts-morph, ts-jest, ts-node, type-fest utilities, custom transformers in many build pipelines. Some of those consume internal APIs that are not guaranteed stable. A native port, even a mechanical one, changes the ABI entirely. The compiler no longer runs in a Node.js process by default, which breaks any tool that imports typescript as a Node module and calls into its checker at runtime.
The team has indicated they will provide API compatibility layers, but this is genuinely the hardest part of the migration. The raw compiler performance is the easy win; the ecosystem surface is where ports like this traditionally accumulate the most friction.
The Broader Pattern
There is a recurring pattern in mature ecosystems. A tool is built in the primary language of its ecosystem, grows successful, and eventually hits the ceiling of what that runtime can provide. Then someone rewrites it in a lower-level language and ships a dramatically faster version.
Ruby had a similar moment with RuboCop and then Rubyfmt. Python is living through it now with Ruff replacing Flake8 and isort for most teams. The JavaScript ecosystem has been in the middle of this transition for several years, with esbuild, SWC, and now the TypeScript compiler itself following the same arc.
What is different here is the scale of the tooling investment. esbuild is one person’s project. The TypeScript compiler is a decade of accumulated type system work, hundreds of thousands of lines of carefully tuned code, and the foundation of tooling that an enormous fraction of web development depends on. Porting it, rather than rewriting it, was the only realistic path that could maintain the semantic fidelity users depend on.
Anders Hejlsberg, who has been with TypeScript since the beginning, characterized the effort as the most impactful thing the team could do for developer experience. That framing is telling. TypeScript’s core value proposition is not its feature set at this point; it is the development experience it provides across millions of codebases. When build times and editor latency are the main friction, a 10x runtime improvement is more impactful than almost any new language feature.
What to Expect
The native port was positioned as shipping in a future major version of TypeScript, with the work in progress on the public repository. The team committed to full type-system compatibility, meaning the Go port and the existing JavaScript compiler would produce identical diagnostics for the same code.
For most developers, the migration will be invisible. Update the package, enjoy faster builds. For teams maintaining custom TypeScript transformers, language server plugins, or tools built on the compiler API, there will be migration work, and the extent of that work depends on how deeply those tools reach into TypeScript internals.
The typescript-go repository is public and has been accepting community observation if not yet broad contribution. Following the issue tracker there gives the best signal on what compatibility gaps remain and what the realistic timeline looks like.
A decade ago, shipping TypeScript itself in TypeScript was the right decision. It demonstrated confidence in the language and kept the team focused on a single stack. Now that TypeScript has proven its value across the industry, the right move is to stop making the compiler a runtime-bound citizen of the same ecosystem it serves. A native compiler that finishes before you can switch windows is a better experience, and better experience is what the project is ultimately for.