How the Go Rewrite of the TypeScript Compiler Achieved a 10× Speedup – Inside Microsoft’s Engineering Details

The Microsoft TypeScript team rebuilt the compiler in Go, setting hard performance goals, choosing Go for its GC and concurrency, prototyping the scanner and parser, redesigning AST structures, and leveraging parallel parsing and independent type‑checkers to cut VS Code compile time from 80 seconds to 7 seconds, a ten‑fold improvement.

TonyBai
TonyBai
TonyBai
How the Go Rewrite of the TypeScript Compiler Achieved a 10× Speedup – Inside Microsoft’s Engineering Details

Background: Hitting the JavaScript Ceiling

Since its 2012 launch, the TypeScript compiler has been self‑hosted in TypeScript, which offers fast iteration but suffers from JavaScript’s single‑threaded execution, high memory overhead of its object model, and the runtime cost of async/await and Promise allocations. Large codebases such as VS Code (1.5 M lines) began to encounter OOM and severe slowdown.

Hard Goals for the New Compiler

Extreme speed : compile to native code, eliminating interpreter and JIT warm‑up.

Shared‑memory concurrency : fully exploit multi‑core CPUs.

Cross‑platform support : run on all major OSes and compile to WebAssembly for browser‑based environments.

Seamless migration : preserve existing TypeScript semantics despite the lack of a formal spec.

Why Go?

Garbage‑collected memory management fits the compiler’s massive, cyclic AST structures, avoiding the manual‑memory burden of Rust.

Syntax and idioms (interfaces, functions) resemble TypeScript, making a direct port feasible.

Gentle learning curve for a team of TypeScript experts.

Native compilation and built‑in concurrency, plus easy WebAssembly target.

Prototype Validation

The team first hand‑translated the scanner and parser to Go. The hand‑written prototype parsed code about five times faster than the original TypeScript version, and the AST produced was structurally very similar, confirming Go’s suitability.

Automated Migration Tool: ts-to-go

Jake Bailey built ts-to-go (https://github.com/jakebailey/ts-to-go) to translate TypeScript constructs: interface

interface
class

struct + methods Complex bit‑wise and logical expressions → equivalent Go code

The tool produces a “run‑but‑ugly” version that accelerates early development.

AST Redesign

JavaScript’s dynamic objects were replaced by a typed hierarchy:

Removed pervasive interface abuse that caused memory‑heavy “fat pointers” and nil‑check hell.

Introduced a base Node struct embedded in all concrete AST nodes, reducing memory use and eliminating nil‑interface issues.

Concurrency as the Performance Engine

The original compiler processed parsing, binding, checking, and emitting sequentially. The Go version parallelizes these stages:

Parsing: each file parsed independently in parallel.

Binding: symbol binding per file runs concurrently.

Type checking: uses “independent checkers” per file group, accepting some duplicate work for massive parallelism.

Result: VS Code compile time dropped from 80 seconds to 7 seconds, a >10× speedup.

Gotchas and Optimizations

Variable shadowing : Go permits inner‑scope shadowing (e.g., err, result), leading to hidden bugs; the team wrote a static analysis tool to catch them.

Method‑value allocations : Passing methods as values (e.g., parser.LookAhead) caused a 17% slowdown; switched back to explicit function calls.

String concatenation : Go’s + copies strings, unlike JavaScript’s optimized cons‑strings, hurting performance in string‑heavy code.

Trade‑offs and Missing TypeScript Features

Moving to Go required sacrificing several TypeScript conveniences:

Compile‑time nil safety via null/undefined types.

Null‑coalescing ( ??) and optional chaining ( ?.) which now need verbose if x != nil checks.

Union types and type narrowing, now expressed via interfaces or large structs.

Generic methods and ternary operators, pending possible support in Go 1.27.

Despite these “pain points,” the performance gains were deemed worth the trade‑off.

Future Outlook

The Go‑based compiler has passed 100 k test cases and is being trialed in internal builds at Slack and Figma (Slack build time reduced from 6 minutes to 40 seconds). Microsoft plans to introduce breaking changes in TypeScript 6.0 to pave the way for the fully Go‑driven TypeScript 7.0.

Note: Go may add generic method support in Go 1.27.

This migration demonstrates that Go can handle large‑scale compiler workloads and provides a reference architecture for teams facing similar performance bottlenecks.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

MigrationTypeScriptASTCompilerConcurrencyGo
TonyBai
Written by

TonyBai

Tony Bai's tech world (tonybai.com). Not satisfied with just "knowing how", we strive for mastery. Focused on Go language internals, high-quality engineering practices, and cloud‑native architecture, exploring cutting‑edge intersections of Go and AI. Gophers who pursue technology are welcome—follow me and evolve with Go.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.