Two weeks ago, Microsoft published the release candidate for TypeScript 6.0, and buried in the announcement is a sentence that changes what this version actually represents. TypeScript 6.0 is described as the last release based on the current JavaScript-implemented compiler. That framing matters more than any individual feature in the release.
TypeScript has compiled itself since the early days. The compiler, the language server, the type checker, all of it written in TypeScript, executed in Node.js, using V8. That self-hosting arrangement is a point of pride in the language’s history, and it is ending with this release.
The Go Rewrite
In early 2025, Microsoft announced that the TypeScript team was porting the compiler to Go. The goals were narrow and explicitly stated: faster builds, lower memory usage, no semantic changes to the type system. The Go port would produce identical output to the JavaScript compiler for the same input, but would do it roughly ten times faster according to the benchmarks Microsoft shared at the time.
Ten times faster sounds like marketing. For TypeScript specifically, it reflects a real bottleneck. The JavaScript implementation runs in a single-threaded V8 process. V8 is fast for a JIT-compiled runtime, but it carries garbage collection pauses, cannot easily parallelize across files without worker threads and coordination overhead, and has memory limits that become visible on large monorepos. Go gives the compiler team native threads, predictable memory layout, and direct control over allocation patterns.
The numbers Microsoft cited were credible because they came with methodology attached. Cold build times on large codebases dropped from minutes to seconds. Incremental builds dropped proportionally. For teams running TypeScript in CI on every pull request, the latency difference between a 90-second type check and a 9-second one changes how engineers actually work.
Why 6.0 Needed to Land First
The Go compiler is not a drop-in swap. Building the parallel architecture required the JavaScript compiler to finish certain groundwork first, and that groundwork landed in the 5.x series and is being finalized in 6.0.
The key piece is isolatedDeclarations. TypeScript 5.5 introduced this flag, which enforces that declaration files (.d.ts) can be generated from each file independently, without needing to see the rest of the project. That constraint sounds restrictive, but it is what makes parallel declaration emit possible. When every file can produce its .d.ts without a global type resolution pass, a multi-threaded compiler can process files in parallel without coordination.
TypeScript 6.0 matures isolatedDeclarations from an opt-in flag to something closer to a well-traveled path. Tooling that has been updated to comply with it will port cleanly to the Go compiler. Tooling that has not will face pressure to update.
This is the pattern TypeScript has followed for major breaking changes throughout its history. The 5.x series softened the ground; 6.0 firms it up. The Go compiler then steps onto firmer ground.
What Gets Removed
Major version bumps in TypeScript historically serve as cleanup releases. TypeScript 6.0 drops several long-deprecated features that have been accumulating since the 4.x era.
The legacy "moduleResolution": "node" setting has been a deprecation target for years. It reflects Node.js’s original CommonJS resolution algorithm and does not understand ESM semantics, package exports fields, or .mts/.cts extensions. The modern replacements are node16, nodenext, and bundler. Removing the old setting forces projects to be explicit about what resolution semantics they expect, which benefits the Go compiler’s ability to reason about module graphs without special-casing legacy behavior.
experimentalDecorators and emitDecoratorMetadata are the other major removal targets. These flags enabled the older, TypeScript-specific decorator proposal. The TC39 standard decorator proposal landed in TypeScript 5.0, and the two systems are semantically different enough that supporting both indefinitely is a maintenance liability. Angular, NestJS, and other frameworks that depended heavily on experimentalDecorators have been migrating to the new model; 6.0 gives the old system an end date.
Older --target values like ES3 are also leaving. The population of environments that genuinely need ES3 output is small enough that a dedicated transpiler like Babel or esbuild handles it better than the TypeScript compiler’s emit pipeline.
The Language Server Question
One aspect of the Go transition that does not get enough discussion is the language server. TypeScript’s editor integration runs through tsserver, a persistent process that does incremental type checking to power autocomplete, error highlighting, rename, and go-to-definition in editors like VS Code, Neovim, and any editor using the Language Server Protocol.
The Go port compiles faster, but the language server experience depends on different latency characteristics. A batch compile benefits linearly from faster throughput; an interactive language server needs low tail latency on incremental operations. The two optimization targets are related but not identical.
Microsoft’s plan has been to port the language server alongside the compiler, but this is the piece that affects every developer’s daily workflow most directly. A faster batch build is visible in CI. A slower or less accurate autocomplete is felt on every keystroke. The TypeScript team is aware of this, and the RC period is partly about giving the ecosystem time to validate that the new implementation matches the existing one’s behavior under real editing conditions.
What TypeScript 6.0 Adds on the Language Side
Beyond the architectural transition, 6.0 ships language features that have been in development through the 5.x cycle. Decorator metadata support, aligned with the TC39 proposal’s final form, is one piece. The decorator metadata proposal allows decorators to access type information at runtime through Symbol.metadata, enabling frameworks to build reflection capabilities without the old emitDecoratorMetadata approach.
// New decorator metadata access via Symbol.metadata
function logged(target: any, context: ClassMethodDecoratorContext) {
const methodName = String(context.name);
context.addInitializer(function() {
// context.metadata is now stable and spec-compliant
(context.metadata as Record<string, string[]>).loggedMethods ??= [];
(context.metadata as Record<string, string[]>).loggedMethods.push(methodName);
});
}
class Service {
@logged
fetch(url: string) { /* ... */ }
}
console.log((Service as any)[Symbol.metadata]); // { loggedMethods: ['fetch'] }
Improved inference in complex generic scenarios has been a consistent thread through recent TypeScript releases, and 6.0 continues that work. The type system’s handling of conditional types, template literal types, and variance annotations has gotten progressively more precise, and the 6.0 RC includes fixes to edge cases that have generated long-standing GitHub issues.
The using and await using declarations from the Explicit Resource Management proposal, which landed in TypeScript 5.2, also get refinements in 6.0. The spec has evolved since the initial implementation, and the 6.0 release aligns the TypeScript behavior with the finalized TC39 text.
The Historical Weight
TypeScript shipped its first public version in October 2012. It was written in TypeScript from the start, which was itself a statement: the language was useful enough that Microsoft was willing to bet their own toolchain on it before the broader ecosystem had committed to it.
Over fourteen years, the JavaScript ecosystem changed around TypeScript in ways nobody predicted. Node.js grew from a niche server runtime to the foundation of nearly all frontend tooling. V8 went from an experimental Chrome engine to the substrate running billions of lines of production code. npm went from a package manager to an infrastructure layer. Through all of that, TypeScript’s compiler remained a Node.js process running JavaScript.
The Go port does not invalidate any of that. The decision to implement in TypeScript made sense in 2012, made sense in 2018, and continued to make sense until the scale of real-world usage made the performance ceiling undeniable. Large codebases at companies running hundreds of microservices, each with full TypeScript type checking in CI, started hitting walls that no amount of incremental build caching could fully solve.
TypeScript 6.0 RC, available now via npm install -D typescript@rc, closes out the JavaScript era of the compiler with a set of changes that prepare the ecosystem for what follows. The self-hosting era is ending in a controlled way: with clear deprecations, a stabilized architecture, and a defined path for the tooling that depends on the current implementation.
That is a reasonable way to end an era. The Go compiler, when it ships as the default, will be judged on its own terms: whether it is faster, whether it is correct, whether the language server feels as responsive. But the version that makes that transition possible is 6.0, and understanding what it closes is as important as understanding what it opens.