TypeScript’s Go Port: Why Go Beat Rust for the Job

Última actualización: 09/01/2026
  • The TypeScript team chose a port to Go over a full rewrite to keep error messages and semantics identical.
  • Go's garbage collection and first-class closures were essential for handling the compiler's complex data structures.
  • Rust's borrow checker would have forced manual workarounds for circular references, adding unnecessary complexity.
  • Go provided mature native code generation and shared-memory concurrency without any extra effort.

TypeScript programming language

When the TypeScript team decided to port their compiler to a new language, they had a clear goal: keep everything working exactly as before. That meant preserving the exact error messages and semantics that developers have relied on for years. According to Anders Hejlsberg, the lead architect, a full rewrite was off the table because it would risk breaking backward compatibility. Instead, they went with a port—and that decision set the stage for a surprising choice: Go over Rust.

The port needed a language that could handle the compiler’s intricate internal structures without forcing major changes. The team quickly realized that garbage collection and first-class closures were non-negotiable. Go offers both out of the box, along with mature native code generation and shared-memory concurrency on every major platform. Rust, on the other hand, would have required significant manual workarounds, especially for the compiler’s circular data structures.

TypeScript 7.0
Related article:
TypeScript 7.0 Ships with Go-Powered Compiler, Delivering Up to 12x Speed Boost

Why Go Over Rust for the Port

Hejlsberg explained that the compiler is full of parent pointers, recursive types, and symbols that refer to each other. These create circular references that are natural in a language with garbage collection. Go’s runtime handles this seamlessly, allowing the team to focus on the port rather than fighting the language. Rust’s borrow checker, while powerful for memory safety, simply does not allow that shape without resorting to unsafe code or reference-counting tricks. That would add complexity and risk, with no clear payoff.

When comparing the two languages, the team found no significant advantage in code generation or concurrency for Rust. Go’s native code generation is already mature, and its goroutines provide a simple, effective model for concurrent execution. Rust’s performance might be slightly better in some edge cases, but the extra effort required to make the compiler work with its ownership rules wasn’t justified. The port needed to be pragmatic, not a showcase of language features.

typescript 6.0 futuro basado en go
Related article:
TypeScript 6.0 and its Go-based future

Compatibility and Semantics: The Top Priority

The primary driver behind the port was maintaining identical behavior. Developers depend on TypeScript’s error messages to debug their code, and any change could break their workflows. By porting to Go, the team could reuse the existing logic and data structures, ensuring that the output remains byte-for-byte compatible. This approach also reduces the risk of introducing subtle bugs that a rewrite might bring.

Go’s garbage collection was a key enabler. The compiler’s internal graph of nodes and references is highly interconnected, and manual memory management would be a nightmare. With Go, the team can allocate and free memory automatically, letting them focus on the compiler’s logic. First-class closures also made it easier to implement the various passes and transformations that the compiler performs, as they can capture context naturally.

Related article:
Solved: next typescript template

Rust’s Borrow Checker: A Dealbreaker

Rust’s borrow checker is designed to prevent data races and memory errors at compile time, but it comes with strict rules. The TypeScript compiler’s data structures are full of cycles and shared references, which the borrow checker rejects unless you use unsafe blocks or Rc/RefCell. Hejlsberg noted that this would force manual workarounds for every circular data structure, adding boilerplate and making the code harder to maintain. There was no codegen or concurrency advantage to justify that extra work.

In the end, the choice was clear. Go offered the right balance of simplicity, performance, and compatibility. The port is now underway, and the team is confident that it will deliver the same TypeScript experience with a faster, more efficient compiler. For developers, this means no surprises—just the same reliable tool they’ve always used, running on a more modern foundation.

All things considered, the decision to pick Go over Rust for the TypeScript port comes down to practical engineering. The need for garbage collection, first-class closures, and seamless handling of circular references made Go the natural fit. Rust’s safety guarantees are impressive, but they come at a cost that the TypeScript team wasn’t willing to pay. The result is a port that preserves everything developers love about TypeScript while laying the groundwork for future improvements.

typescript
Related article:
TypeScript 5.9: Enhanced Developer Experience and Glimpse at the Future
Related posts: