· 6 min read ·

TypeScript 6.0: The Go Compiler Rewrite Is the Real Story

Source: typescript

Major version bumps in TypeScript tend to arrive with a mix of new language features and some housekeeping: deprecated options removed, stricter defaults, things that were soft errors becoming hard errors. TypeScript 6.0 does all of that, but it also ships something more significant: a native compiler written in Go, which the team has been building as microsoft/typescript-go since the project was announced in early 2025.

That combination makes TypeScript 6.0 a different kind of release. The language changes matter for code you write today, but the compiler rewrite changes what TypeScript tooling can be over the next several years.

Why Rewrite the Compiler in Go

The original TypeScript compiler is written in TypeScript, which is an elegant choice for a self-hosting language but not an efficient one for a compiler that needs to check millions of lines of code. JavaScript is fundamentally single-threaded, and while V8 does an impressive job of optimizing the runtime, the TypeScript compiler’s workload involves complex graph traversals, cross-file symbol resolution, and type inference that cannot parallelize within a single process.

For small to medium projects, this has never been a real problem. Run tsc --watch and it responds fast enough to be invisible. At the scale of large monorepos, though, with tens of thousands of source files and hundreds of interconnected packages, the constraints become visible: build times measured in minutes, editor latency as the language server tries to keep up with changes, and teams working around the bottleneck with project references, skipLibCheck, and layered build caches.

Go addresses these constraints directly. It is a compiled, statically typed language with native goroutines for concurrency, which means the type checker can fan out across files in parallel in ways the JavaScript implementation cannot. The TypeScript team cited roughly 10x performance improvements for full builds on large codebases. Incremental builds see smaller gains because the bottleneck there shifts to I/O and cache validation, but cold builds and CI pipelines where caching is unreliable benefit substantially.

The Go port implements the same semantics as TypeScript 5.x, designed to drop in as a replacement for tsc. Your existing tsconfig.json works unchanged, and type checking behavior should be identical. The repo has been public since the announcement, and the team has been tracking compatibility issues openly, which has given the ecosystem some lead time to identify gaps before the 6.0 release.

What the Native Compiler Means for Tooling

The performance gains extend beyond raw build speed. The language server that powers editor features, including autocomplete, go-to-definition, find all references, and inline type display, runs the same type checker under the hood. When the checker is faster, the language server becomes more responsive on large files and codebases. The 10x number applies to full project builds; interactive queries in the language server should see meaningful improvements for projects that were previously slow to respond.

A faster native compiler also makes isolatedDeclarations more practical. That flag, introduced in TypeScript 5.5, requires that declaration files (.d.ts) can be generated for each file independently, without needing to type-check the entire program. The motivation was enabling build tools to parallelize declaration emit across files. With a slow JavaScript compiler, the parallelism gains were partially offset by process startup costs. With a native binary, that trade-off shifts.

Tools like Vite and esbuild have long stripped TypeScript types without invoking tsc, trading full type checking for speed. The Go compiler narrows the gap between those two modes: you can have both correctness and performance without choosing a transpile-only pipeline. For teams that disabled type checking in their build pipeline for speed reasons, TypeScript 6.0 gives them a reason to revisit that decision.

Language-Level Changes

Beyond the compiler infrastructure, TypeScript 6.0 removes a collection of options deprecated across the 5.x series.

The --importsNotUsedAsValues and --preserveValueImports flags, which controlled how import statements were treated at emit time, are gone. They are replaced by the unified --verbatimModuleSyntax flag, which has cleaner semantics: if you write a type-only import, you must mark it as such with import type, and the emitter respects that annotation literally. If your tsconfig.json still references the old flags, the compiler errors; the fix is mechanical.

Support for --target ES3 and --target ES5 is removed. These targets dated from an era when TypeScript’s job was partly to downcompile syntax, before the broader ecosystem settled on transpilers and bundlers as the right place for that work. Removing them simplifies the compiler’s emit logic and reflects a pragmatic reality about what codebases TypeScript is actually used on.

TypeScript 6.0 also ships --erasableSyntaxOnly, which restricts TypeScript code to forms that can be type-erased without transformation. Under this flag, features that require the compiler to emit different JavaScript than what the TypeScript source implies are disallowed. The practical cases are legacy namespaces, const enums with computed values, and decorator metadata.

Here is what the distinction looks like. This is fine under --erasableSyntaxOnly, because removing the type annotation leaves valid JavaScript:

function greet(name: string): string {
  return `Hello, ${name}`;
}

This is not, because const enum values are inlined at their use sites during compilation, which is a transformation rather than erasure:

const enum Direction {
  Up,
  Down,
}

const d: Direction = Direction.Up; // emitted as: const d = 0;

The motivation connects to the TC39 Type Annotations proposal, which aims to define a subset of type syntax that JavaScript engines can parse and skip at runtime without a compilation step. TypeScript 6.0 does not require compatibility with that proposal, but --erasableSyntaxOnly gives teams a way to opt in and verify their codebase is already on the right side of the line. Libraries targeting broad compatibility have the most reason to adopt it.

Migration Considerations

For most TypeScript projects, upgrading to 6.0 means addressing removed compiler options, checking for use of removed emit targets, and running tsc to surface previously-soft errors. The --target removal is the most likely to surprise teams that have had the same tsconfig.json for a few years without revisiting it.

The Go compiler ships as the new tsc binary. Build tools that invoke tsc directly will pick up the new binary automatically. The rough edge is the TypeScript compiler API, which tools like ts-morph, ts-jest, and various ESLint plugins rely on to programmatically traverse and modify TypeScript programs. That API is not immediately available in the Go port; the JavaScript compiler remains available as a fallback during the transition. Tooling authors should track the typescript-go issue tracker for the API roadmap, because the migration window will not stay open indefinitely.

The JavaScript compiler is not going away overnight, but it is no longer the primary path. Teams building TypeScript tooling should treat this release as the start of a migration window, not a distant concern.

The Broader Shift

TypeScript’s position in the JavaScript ecosystem has been remarkable to watch. It went from a Microsoft project that the broader JavaScript community was skeptical of to the de facto type system for serious JavaScript development, embedded in major frameworks, required in many organizations, and influential enough that TC39 is now considering native type annotation syntax for JavaScript itself.

That growth creates the performance pressure the Go rewrite addresses. TypeScript is no longer a small overlay on a codebase; it is the check that runs on every CI build, the server that powers every editor interaction, the tool that processes millions of lines on every developer machine. Scaling that workload requires a different kind of implementation than a self-hosted JavaScript compiler can provide.

Language features ship in every TypeScript release. A new compiler runtime ships once. TypeScript 6.0 is worth paying attention to because of which of those two things is the larger change.

Was this interesting?