← Back to Index
October 24, 2026•Web Development

tsrs vs. tsgo: How a Rust Port of the TypeScript 7 Compiler Beat Microsoft's Go Rewrite by 4x to 12x

The Compiler Premise That Just Cracked

When Microsoft announced in 2025 that the native TypeScript 7 compiler (tsgo) would be written in Go rather than Rust, the reasoning sounded bulletproof. TypeScript lead Ryan Cavanaugh explained that TypeScript 7 was a mechanical port, not a ground-up rewrite. Because the legacy JavaScript compiler relied heavily on cyclic abstract syntax trees (ASTs), bidirectional symbol tables, and millions of short-lived heap allocations, Microsoft argued that Go's garbage collector and structural similarity to JavaScript made it the only practical target. Rust's strict ownership and borrow checker, the argument went, would turn a 1:1 port into a multi-year architectural quagmire.

On October 20, 2026, that premise was dismantled in public.

Cloudflare and VoidZero (the team behind Oxc, Rolldown, and Oxlint) announced tsrs&source=gmail&ust=1792890549156000&sa=E, a function-by-function Rust port of Microsoft's Go-based TypeScript 7 compiler. Originated by Max Schwenk at Owner.com, merging with Theo Browne's tsc-rs effort, and now incubated under oxc-project/tsrs, tsrs passes 13,458 of 13,462 upstream TypeScript error baselines (99.97% conformance) while running 4.37x faster on average—and up to 11.81x faster on large monorepos—using 70% less RAM at identical thread counts.

Inside the Benchmarks: Equal Threads, Massive Gains

Viral social media posts quickly threw around 16x to 18x speedup claims, largely because tsrs defaults to scaling up to 16 checker threads while tsgo defaults to 4. However, when we strip away thread-count differences and evaluate the verified 17-project benchmark suite on a 16-vCPU AMD EPYC machine with both compilers locked to --checkers 4, the engineering reality is still staggering:

  • mui-docs: Drops from 12.69s on tsgo 7.0.2 to 1.07s on tsrs (11.81x speedup), while peak memory plunges from 7.61 GiB to 1.05 GiB (a 7.2x reduction).
  • vscode: Drops from 10.94s on tsgo 7.0.2 to 2.21s on tsrs (4.95x speedup), with peak RAM falling from 7.49 GiB to 1.80 GiB.
  • supabase-studio: Drops from 7.24s to 1.31s (5.53x speedup).
  • Owner.com's 37,000-file Monolith (Olympus): Drops from 61.4s and 36.0 GiB of RAM on 4-thread tsgo 7.0.2 down to 8.75s and 6.1 GiB on 4-thread tsrs (and 4.24s at default 16 threads).

Where developers and AI coding agents feel the difference most acutely is incremental --watch mode. On the VS Code repository, a single-line edit or leaf-file type error takes 1.65s to 1.76s to re-verify in tsgo. In tsrs, that same incremental check finishes in 31ms to 34ms—a 53x latency reduction that transforms type checking from a noticeable pause into an imperceptible keystroke event.

How tsrs Bypassed the Borrow Checker: Arenas and 32-Bit Handles

How did a small effort port 37 Go packages into 37 Rust crates (1 file : 1 file) without getting paralyzed by Rust lifetime annotations on cyclic AST references?

They refused to play the lifetime game. Instead of threading 'a lifetimes through thousands of recursive checker functions, tsrs places all AST nodes, symbols, and types inside custom memory arenas and passes around compressed 32-bit handles (P<T>):

// Conceptual representation of tsrs arena handle architecture
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct P<T> {
    index: u32, // 32-bit compressed offset into the arena slab
    _marker: core::marker::PhantomData<T>,
}

impl<T> P<T> {
    #[inline(always)]
    pub fn get(self) -> &'static T {
        // SAFETY: Arena slabs remain pinned for the duration of the check phase;
        // leaf file arenas are explicitly reclaimed only after checker completion.
        unsafe { ArenaRegistry::resolve_unchecked(self.index) }
    }
}

By using 32-bit indices (P<T>) instead of 64-bit raw pointers or garbage-collected Go heap objects, tsrs unlocked a cascade of systems-level optimizations:

  1. Pointer Compression & Cache Locality: Cutting handle sizes from 64 bits to 32 bits shrinks struct layouts by 14% to 21%, packing vastly more AST nodes into L1/L2 CPU caches.
  2. Deterministic Leaf-File Deallocation: Whereas Go's garbage collector accumulates millions of small objects across the heap until GC sweeps trigger, tsrs frees leaf-file arenas immediately after type-checking completes, cutting memory by 13% to 16%.
  3. Lazy JSDoc & Member Resolution: Deferring JSDoc parsing and symbol member resolution until explicitly queried eliminated 21% to 23% of unnecessary allocations.
  4. Work-Stealing Checker Threads: Rather than statically partitioning files across 4 workers, tsrs uses work-stealing queues across checker threads and deterministically sorts diagnostics afterward so output remains byte-identical regardless of thread scheduling.

The AI Porting Loop—and the Upstream Feedback Effect

Equally fascinating is how tsrs was constructed. Max Schwenk initiated the port because running multiple AI coding agents locally against Owner's 37,000-file repository consumed over 50 GiB of RAM across tsgo and LSP instances, crashing 36 GiB MacBooks.

Rather than hand-writing Rust, Schwenk orchestrated more than 20 parallel Claude subagents, assigning each agent a dedicated crate in an isolated Git worktree. The human engineering effort went into designing the architecture rules (P<T> handles, zero lifetime annotations, isolating unsafe inside tsrs_core verified under Miri) and enforcing an automated verification harness: every agent-proposed optimization was automatically rejected unless it reduced instructions by 1%, wall time by 2%, or memory by 5% while maintaining byte-identical output against Microsoft's 13,462 upstream test baselines.

To Microsoft's credit, the TypeScript team has responded pragmatically rather than defensively. On Hacker News, TypeScript PM Daniel Rosenwasser noted that many of the discoveries in tsrs are algorithmic rather than exclusive to Rust—and confirmed that Microsoft has already upstreamed several tsrs optimizations into TypeScript 7.1 Beta, cutting the performance gap significantly.

Why Cloudflare and VoidZero Are Betting on Rust

If Microsoft is already porting tsrs optimizations back into tsgo, why did Cloudflare and VoidZero step in to maintain oxc-project/tsrs long-term? Two strategic realities make Rust irreplaceable for their stack:

  • First-Class WebAssembly (@tsrs/wasm): Compiling a Go binary to WebAssembly requires bundling Go's garbage collector and runtime scheduler into every WASM module, creating severe memory overhead and clunky host-guest interop. Rust's zero-runtime memory model compiles cleanly to wasm32-wasip1, allowing Cloudflare to run full TypeScript type-checking inside browser sandboxes, edge workers, and lightweight AI agent containers.
  • Single-Process Type-Aware Linting in Oxlint: VoidZero's toolchain—Vite, Rolldown, Oxlint, and Oxfmt—is built in Rust. Previously, running type-aware lint rules required spawning an external TypeScript process and serializing AST metadata across IPC boundaries. With tsrs available as a native Rust library crate, Oxlint can execute type-aware linting in a single unified memory space.

How Engineering Teams Should Adopt tsrs Today

While tsrs is a massive leap forward, it is still a weeks-old project that relies on unsafe arena invariants and currently lacks native Windows --watch support and 10% of fourslash LSP edge cases. Do not rip out tsc as your sole production deployment gate tomorrow. Instead, adopt a two-tier workflow:

{
  "scripts": {
    "typecheck:fast": "tsrs --noEmit",
    "typecheck:gate": "tsc --noEmit",
    "watch:types": "tsrs --watch --noEmit"
  }
}

Use npx tsrs --noEmit in local watch mode and inside AI coding agent verification loops where 30ms feedback cycles save hours of developer waiting, and keep tsgo (tsc) as your final blocking merge check in CI until tsrs and TypeScript 7.1 stabilize later this quarter.

Build something exceptional.

Custom web design and development, no templates.

Start a Project
tsrs vs tsgo: Rust TypeScript Compiler Benchmarks — ZIAFTRA