The number everyone quotes is real. The TypeScript team's own benchmark shows VS Code's 1.5-million-line checkout going from 77.8 seconds to 7.5 seconds under the native compiler. Playwright drops from 11.1s to 1.1s. Editor load on the VS Code project falls from 9.6s to roughly 1.2s, and memory use lands at about half. Those are not vendor slides. Teams at Figma, Google, Linear, Notion, Slack, and Vercel reported similar cuts during the preview.

Here is the line that gets skipped. The first time you run import ts from "typescript" against the thing that produced those numbers, it does not work. TypeScript 7.0 is a Go program. The in-process API that the entire tooling layer was built on does not cross that boundary, and it will not have a stable replacement until 7.1.

What actually shipped in April

The native compiler is the project the team codenamed Corsa. It is a port of the TypeScript checker, emitter, and language service from TypeScript to Go. The beta landed on April 21, 2026, distributed as @typescript/native-preview@beta and invoked through a new binary, tsgo, which becomes plain tsc when 7.0 reaches stable. A release candidate is expected a few weeks out, with stable inside a couple of months.

TypeScript 6.0, which shipped in February, is the last release written in JavaScript. It exists mostly as a bridge. It carries deprecations and option changes forward so that by the time you reach for tsgo, your config already matches what the Go compiler expects. The 7.0 beta also drops a pile of legacy surface, including the ES5 target, downlevelIteration, AMD and UMD module output, baseUrl, and namespace-style module blocks.

None of that is the interesting part. The compiler running faster is expected. The compiler changing what language it is written in is what reshapes the ecosystem, because a large slice of the JavaScript toolchain does not call tsc on the command line. It imports the TypeScript package as a library and asks it questions.

The API that quietly held everything together

The typescript npm package was always two things at once. It is the tsc CLI, and it is a programmatic interface the team now calls the Strada API, after the codename of the JavaScript implementation. That API exposes the parser, the type checker, the transform pipeline, and the language service as plain JavaScript objects you can construct and walk inside your own process.

A surprising amount of tooling leans on this, and most engineers never think about it. typescript-eslint builds its type-aware rules by asking the checker for the type at a node. ts-morph wraps the compiler API to make codemods ergonomic. Custom transformers plug into the emit pipeline through ts-patch or the older ttypescript. Bundler plugins, documentation generators, and editor extensions all reach for the same in-process objects.

That pattern works because everything lives in one V8 process. Your tool and the compiler share a heap. You hand the checker an AST node, it hands you back a Type, and you read its properties synchronously with no serialization in between.

A Go binary breaks that arrangement at the foundation. Go has its own runtime, its own garbage collector, and its own memory. You cannot require() it into Node and pass object references back and forth. The shared heap that made the Strada API cheap is gone, and no amount of API design brings it back inside a single process.

Isometric editorial illustration contrasting shared memory (left) with IPC (right).

The replacement is a process boundary, not a function call

The new programmatic interface ships as @typescript/api, and it rests on a different assumption. Instead of importing the compiler, you talk to a long-lived TypeScript process and exchange messages with it. The team describes a foundation that runs over standard I/O, so a consumer in any language can drive the compiler through inter-process communication.

To keep the common case tolerable for Node tools, the team wrote libsyncrpc, a native Node module in Rust that allows synchronous calls into the child process. Sit with that detail. The fast new TypeScript compiler is in Go, and the shim that lets Node talk to it synchronously is in Rust. The single-language world, where one npm install gave you both the CLI and a library, is over.

There is an honest cost here that the speed numbers hide. Crossing a process boundary means serializing requests and responses. Asking for one type is cheap when it is a property read on a shared object. It is not free when it is a round trip with marshalling on both ends. The native checker is fast enough that this trade still wins on whole-program work. For a tool that makes millions of fine-grained queries, the boundary is a new line in the performance budget that did not exist before.

Editors are the exception worth calling out, and they explain why VS Code adapts cleanly while npm libraries do not. An editor never imported the compiler in-process. It already talked to a separate tsserver process over a JSON protocol, so swapping the JavaScript server for a Go one is mostly a matter of pointing at a new binary. The pain is concentrated on tools that took the shortcut editors never could: importing the compiler as a library and reading its objects directly.

The team has been clear about the timeline. A stable programmatic API is not expected until at least 7.1, several months after 7.0 ships. So the first stable native release is the fast checker and CLI, full stop. The library that tools depend on arrives later, on its own schedule, with a surface that is still being designed. That gap is not an oversight. Shipping the checker first lets the largest, simplest win land while the harder question, what a cross-language compiler API should even look like, gets more time.

Who pays for the rewrite

The bill lands unevenly. If you only ever run tsc to type-check and emit, you are mostly fine, and you collect the speedup. If you ship anything that consumes the compiler as a library, the architecture under you changed.

Tool categoryHow it used the old APIWhat changes under 7.0
typescript-eslintSynchronous checker queries, plus a TS-AST to ESTree conversion per nodeType info now sits behind an async, out-of-process boundary; ESLint core does not yet support async parsers
ts-morphThin ergonomic wrapper over the in-process compiler APINo in-process compiler to wrap until the new API stabilizes
Custom transformers / ts-patchHook into the JS emit pipeline at compile timeThe Go emitter exposes no JS transformer hooks; the patchable surface keeps shrinking
Bundler & docs pluginsImport typescript and walk types directlyMust move to message passing, or stay pinned to 6.0 for that step

typescript-eslint is the clearest case, because it sits in nearly every serious TypeScript repo and its expensive rules are exactly the type-aware ones. The maintainers have said the native compiler will most likely be consumed through a native binding that is asynchronous, and ESLint does not yet support asynchronous parsers. Adopting the fast checker for linting is blocked partly on a change inside ESLint itself, not just on TypeScript shipping an API.

Their response is telling. Rather than bolt the Go compiler onto the existing Node pipeline, they prototyped tsgolint, a linter written in Go that runs directly on the native compiler's AST and skips the ESTree conversion entirely. In their measurements it runs 20 to 40 times faster than ESLint with typescript-eslint, across 40 type-aware rules. It is explicitly a prototype, not under active development, and missing config files, editor integration, and the plugin ecosystem that makes the current setup usable.

tsgolint matters less for what it does today than for what it signals. The natural way to be fast next to a Go compiler is to live inside the Go process, not to call across the boundary. If that logic holds for linters, it holds for codemod engines and type-aware bundler plugins too. The pressure points the whole ecosystem toward native, and away from the JavaScript-library model that defined TypeScript tooling for a decade.

Why Go, and why that explains the API problem

The choice of Go surprised people who assumed Rust, given how much JavaScript tooling now sits on Rust. Anders Hejlsberg's stated reasoning, repeated across interviews after the announcement, was portability of the existing code. The TypeScript compiler is a large body of function-heavy, closure-heavy JavaScript that walks deeply cyclic graphs. Go's structural fit to that style, its garbage collector, and its goroutine concurrency made a near-mechanical port plausible.

Rust's ownership model fights cyclic graph data structures, which is most of what a type checker is. A faithful Rust port would have meant rethinking the data model, not transcribing it. That is partly why earlier independent attempts at a Rust TypeScript checker stalled. The language complexity is brutal regardless of the host, and the borrow checker adds friction exactly where the checker needs shared mutable graphs. Go let the team keep the algorithms and change the runtime.

The decision that made the port fast is the decision that broke the API. Choosing any non-JavaScript language for the compiler guarantees a runtime boundary between it and the Node tools that consume it. Go is not special here. Rust or C# would have built the same wall. The speed and the API disruption are two faces of one choice, and you do not get the 10x without giving up the shared heap.

Isometric conceptual illustration of diverging glowing tracks with barrier gates.

What to actually do before 7.1

The pragmatic posture for the next few months is to split the toolchain by job, and adopt the fast compiler only where it has already matched the old one.

Start with no-emit type checking. That is where the native compiler is most proven and where the win is largest, since CI type checks and editor responsiveness are what hurt on big repos. Run tsgo --noEmit as a separate check while leaving everything else on 6.0.

Keep tsc 6.0 for the jobs that still depend on the JavaScript library: declaration emit for published packages, anything driven by custom transformers, and any tool that imports the compiler API. The two coexist. A common pattern installs the native preview under an npm alias so tsgo and the classic tsc live in the same project, with each wired into the script that needs it. For a sequenced version of this, the TypeScript 7 beta migration guide walks through keeping CI green while you move checks over one at a time.

Audit your dev dependencies for compiler-API consumers before you assume a clean cut. Grep the lockfile and your build scripts for the ones that matter: typescript-eslint, ts-morph, ts-patch, ttypescript, and any bundler plugin that reads types. Each is a reason to keep 6.0 installed, not a blocker on type-checking with the fast compiler.

This native-tooling shift is not isolated to TypeScript. The same forces produced a Rust bundler under Vite, a Go-based esbuild, and a broader move to compile JavaScript infrastructure in systems languages, which is also why the runtime layer is being rewritten beneath you. The Bun versus Node decision framework covers the runtime side of the same story. TypeScript is just the most load-bearing example, because so much tooling treats the compiler as a library rather than a binary.

The real milestone is 7.1

Treat 7.0 stable as half the release. The fast checker and CLI arrive first, and for most teams that alone justifies adopting it for type checking. The part that decides how the ecosystem reorganizes is the stable programmatic API in 7.1, because that is what tells every tool author whether to rebuild over an IPC boundary, move into the Go process, or stay on 6.0 indefinitely.

The open question is whether a message-passing API can ever feel like the old in-process one, or whether the fine-grained, synchronous, walk-the-type-graph style of tooling is simply ending. If it is ending, the next few years of TypeScript tooling get rewritten in Go and Rust, closer to the metal and further from the install-a-library convenience that defined the era we are leaving. The 10x is the headline. The architecture underneath it is the story.