· 6 min read ·

TypeScript 7.0 Beta: What Giving Up Self-Hosting Actually Buys You

Source: typescript

The TypeScript 7.0 Beta announcement dropped this week, and the headline is striking: the TypeScript compiler is no longer written in TypeScript. After roughly a year of porting work, the team has moved the entire codebase to Go, producing a native binary that runs without Node.js and checks types somewhere in the neighborhood of ten times faster than the previous compiler on large projects.

That’s the summary. The more interesting question is what had to be true about the old architecture for this move to be worth doing at all.

The Cost of Self-Hosting

For most of its life, TypeScript has been a bootstrapped language: the compiler was written in TypeScript and compiled itself. This is an elegant property. It means the team dogfoods their own type system, the compiler serves as a living reference implementation, and contributors only need to know one language. Plenty of mature compilers work this way. The Go compiler itself was written in Go. Rust was originally written in OCaml before being rewritten in Rust.

But self-hosting has a ceiling. A JavaScript runtime sits beneath every invocation of tsc. V8 is genuinely fast, but it’s a garbage-collected, JIT-compiled environment optimizing for a different workload than batch type checking. The TypeScript compiler can’t use OS threads for parallelism because JavaScript is single-threaded. It can’t preallocate memory in bulk or control object layout. It can’t avoid GC pauses during large project loads. These aren’t bugs; they’re consequences of running in a managed runtime.

For a while, none of this mattered much. TypeScript projects were small. Then they weren’t. Monorepos with hundreds of thousands of lines of TypeScript became common, and tsc --watch latencies that were tolerable on a 10k-line codebase became genuinely painful at 500k lines. The team spent years optimizing within the constraints, shipping incremental builds, project references, and smarter caching. Those improvements are real and still present in 7.0. But they were working against a structural limit.

Why Go

When Microsoft first announced the port in early 2025, the Go choice raised eyebrows. The ecosystem had spent several years watching tools rewrite themselves in Rust: ripgrep, esbuild (no, esbuild is Go actually), Biome, Rolldown, the Rust-based linter wave. Rust would seem like the natural pick.

The team’s reasoning, as Anders Hejlsberg explained it, came down to two things: the structural similarity between Go and TypeScript’s existing codebase, and Go’s concurrency model.

TypeScript’s compiler is heavily object-oriented. Nodes, symbols, types, and diagnostics are all heap-allocated objects with lots of pointer relationships. Go’s garbage collector handles that pattern well, and Go’s struct and interface model maps cleanly onto TypeScript’s class hierarchy. A mechanical port, file by file, was feasible in a way that a Rust port probably wouldn’t have been. Rust’s ownership model would have required rethinking the entire data structure graph, since the existing compiler freely creates cycles between AST nodes and type objects. That’s not a Rust-friendly pattern without heavy use of Rc<RefCell<...>> everywhere, which gives up most of Rust’s performance advantages anyway.

Goroutines gave the team something they couldn’t get in either JavaScript or Rust without significant design work: cheap, structured concurrency. Type checking individual files is largely independent once the dependency graph is resolved. Go makes it straightforward to fan out that work across all available cores without fighting the borrow checker or managing thread pools manually.

What Ten Times Faster Looks Like

The benchmark numbers Microsoft shared show roughly a 10x reduction in check time on large projects. For a codebase that previously took 30 seconds to full-check from scratch, that’s 3 seconds. For editor responsiveness on incremental changes, the improvement is sometimes more dramatic because the bottleneck shifts from CPU to I/O.

The tsc binary in TypeScript 7.0 is a standalone executable. No Node.js installation required. This has concrete implications for CI: you no longer need a Node.js step just to type-check a project. Docker images for type checking can be smaller. Projects that use TypeScript purely as a build-time checker don’t need to carry a JS runtime in their toolchain.

The language server, tsserver, is also being rewritten. The existing tsserver protocol is preserved, so VS Code, Neovim, and other editors that speak LSP won’t need changes to benefit. The performance gains show up automatically once the extension updates to the new server binary.

What Stays the Same

None of the type system semantics change. Every type-level feature introduced through TypeScript 5.x, including variadic tuple types, template literal types, satisfies, using declarations, and the rest, is preserved with identical behavior. The TypeScript team has been explicit that this is a mechanical port, not a redesign. The Go code implements the same algorithms as the TypeScript code it replaced.

Declaration files (.d.ts) remain the interchange format. The module resolution algorithm, tsconfig.json schema, and --noEmit / --declaration / --isolatedModules flags all behave identically. The compiler still emits the same JavaScript. From a project configuration standpoint, upgrading to 7.0 is expected to be largely mechanical.

There are breaking changes documented in the beta notes, but they’re in the category of corrected behavior rather than intentional removal. Edge cases where the old compiler was type-checking incorrectly, fixed. That’s normal for a major version.

The Broader Pattern

TypeScript’s move fits into a pattern that’s been accelerating for several years. Tooling that runs in a hot loop on developer machines has been migrating to native runtimes. esbuild demonstrated in 2020 that a Go-based bundler could be 10-100x faster than webpack for a certain class of workloads. Biome (originally Rome) brought Rust to formatting and linting. Oxc and Rolldown are rebuilding the JavaScript toolchain in Rust. Bun reimplemented the Node.js runtime in Zig.

The common thread is not ideology about systems languages. It’s that JavaScript-based tooling has structural performance limits that matter more as projects scale, and native tooling can sidestep those limits. TypeScript 7.0 is the logical endpoint of this trend applied to the type checker itself.

What’s notable about the TypeScript case is the scope. This isn’t a third-party tool reimplementing TypeScript’s functionality; it’s the official implementation, maintained by the same team, with full semantic compatibility guaranteed. That’s different from, say, using SWC or esbuild as a faster-but-not-type-safe alternative. You get the speed without giving up correctness.

Implications for the Ecosystem

Some tooling will need updates. Tools that wrap tsc as a Node.js module by importing the TypeScript compiler API directly (import * as ts from 'typescript') are in a more complicated position. The native port exposes a JavaScript-compatible API layer for this use case, but there may be gaps in the beta. Tools like ts-jest, ts-node, and various webpack/rollup plugins that programmatically invoke the compiler will need to test against 7.0 before recommending upgrades.

For most end users, the upgrade path is: update typescript in package.json, run tsc, fix any newly-caught type errors that were previously missed, done. The beta is the time to do that testing and file issues.

Editor extensions will pick up the new language server over the coming weeks. The VS Code TypeScript extension ships its own bundled version of TypeScript, so that upgrade will happen independently of your project’s TypeScript version.

What the Beta Means

The beta label means there are known rough edges. The team has flagged that some compiler API surface used by plugins may not yet be fully implemented in the Go port, and performance characteristics on edge-case projects are still being profiled. Incremental build (--incremental) and project references are present but the team has called out potential regressions to watch for.

Filing issues against the beta matters more than usual here because the architectural shift means the team is validating a complete reimplementation, not just new features layered on a known-good base. If you maintain a library with non-trivial TypeScript types or a plugin that touches the compiler API, running your test suite against the 7.0 beta and reporting what you find is genuinely useful.

You can install the beta today:

npm install --save-dev typescript@beta

Or for a one-off check:

npx typescript@beta --version

The stable release timeline hasn’t been announced, but based on the beta announcement the team is targeting broad compatibility testing through the beta period before cutting a release candidate.

A compiler that started as a JavaScript experiment in 2012, became a serious production tool, and has now outgrown its own implementation language is a reasonable arc for a project to follow. The more interesting question is what the team does with the headroom that ten times faster buys them.

Was this interesting?