tsrs: A Rust Port of TypeScript That Is Up to 8x Faster

·12 min read·Evergreen Tools Team

The type checker is the slowest step in most JavaScript projects. You change one line, then wait forty seconds during which you are neither writing code nor thinking about architecture. In October 2026, Cloudflare and VoidZero, working with Max Schwenk of Owner.com, announced tsrs: a Rust port of the TypeScript compiler, incubated inside the Oxc toolchain and built in close collaboration with the TypeScript team. The early benchmark is up to 8x faster and roughly 70% less memory than TypeScript 7.0 at the same number of type check workers. This piece does not sell that number. It covers three things: what "not a fork" actually means, how to measure the real gap on your own repository, and what to run before this touches your CI.

Source code on a dark screen

A type checker is the slowest thing in most JavaScript CI runs

1. First, Correct a Misunderstanding: It Is Not a Fork

Cloudflare states it plainly: we are not forking TypeScript. How the language is designed and where it goes stays with the TypeScript team. That posture is not politeness, it is engineering necessity. Many TypeScript features spend years in deliberate prototyping before an implementation is chosen, and a Rust port has to follow those decisions closely or it will drift and eventually break compatibility. So tsrs is framed as a faithful port: it reproduces semantics strictly from the original implementation rather than quietly improving them. The practical consequence for you is that this is a faster implementation to evaluate, not a TypeScript that does new things. Any port claiming to be a smarter TypeScript deserves a raised eyebrow.

# Try the experimental release. It is a faithful port, not a fork.
npm install tsrs
npx tsrs --version

# Run it over a real project
time npx tsrs --noEmit -p tsconfig.json

2. Where the 8x Comes From, and What It Does Not Say

The official framing is specific: compared against TypeScript 7.0 with the same number of type check workers. That last clause matters, because the recent wave of TypeScript ports has produced a lot of performance claims built on larger machine types or options that differ from TypeScript's defaults. Cloudflare says the comparison configuration was confirmed with the TypeScript team, and the harness is public in the repository's bench directory. Two other numbers are more interesting than the headline. TypeScript 7.1 beta is already up to 2.5x faster than 7.0, and some of those optimizations were upstreamed from tsrs. And tsrs ships an adaptive configuration that depends on machine type, trading more memory for more speed. Your real number is your repo times your CI box times your tsconfig. Someone else's chart is evidence that a difference can exist, not a promise about yours.

# Measure both checkers on the SAME project, same worker count.
# Do not trust a vendor chart; your repo is the only benchmark that matters.

collect() {
  label=$1; shift
  /usr/bin/time -v "$@" 2>&1 >/dev/null \
    | grep -E "Elapsed \(wall|Maximum resident" \
    | sed "s/^/$label  /"
}

collect tsc  npx tsc --noEmit -p tsconfig.json
collect tsrs npx tsrs --noEmit -p tsconfig.json

# Repeat 3 times on a warm cache and take the median. A single cold run
# on a laptop will happily show you a 30% difference that is pure noise.

3. Measure It on Your Own Repository

The second snippet does one job, under two rules: same project, same worker count. Run it three times on a warm cache and take the median. This feels fussy until you remember that a single cold run on a laptop will cheerfully invent a 30% gap that is pure noise. There is a step that gets skipped constantly: confirm what tsconfig.json actually includes. A lot of "slow type checks" are not a slow engine but an include list that drags in node_modules or build output. If tsc is slow because it is checking files it should never have been checking, swapping engines will not save you. Fix the scope first, then benchmark.

# Parity before speed: emit machine-readable diagnostics from both and diff them.
npx tsc  --noEmit --pretty false -p tsconfig.json > /tmp/tsc.txt  || true
npx tsrs --noEmit --pretty false -p tsconfig.json > /tmp/tsrs.txt || true

# Sort so ordering differences do not drown real differences
sort /tmp/tsc.txt  > /tmp/a.txt
sort /tmp/tsrs.txt > /tmp/b.txt
diff /tmp/a.txt /tmp/b.txt | head -40

# The number to watch is not "did it run", but
# "did it report the same errors on the same lines".

4. Parity Before Speed: Compare Diagnostics, Not Vibes

Run both checkers with --pretty false, sort the output, then diff. The sort is not optional: error ordering differs between implementations all the time, and an unsorted diff is mostly noise. The metric you actually care about is one sentence long: does it report the same errors on the same lines? If diagnostics match exactly, you can honestly say it is only faster. If it reports more or fewer, you need to know which rule class diverged before you accept it. This mirrors how Cloudflare maintains Vinext in sync with upstream Next.js: alignment with upstream is a continuous process, not a one-time sign-off.

# Keep it out of your required CI job until parity holds.
# .github/workflows/typescript.yml
- name: Type check (authoritative)
  run: npx tsc --noEmit -p tsconfig.json

- name: Type check (tsrs shadow)
  continue-on-error: true
  run: npx tsrs --noEmit -p tsconfig.json

- name: Record type-check duration
  run: |
    /usr/bin/time -f "tsc_wall_seconds=%e" npx tsc --noEmit -p tsconfig.json

5. Why Rust and Not Go

Fair question. The TypeScript team's own Go port already made TypeScript around 10x faster with a far smaller memory footprint, so why does this exist? Cloudflare's answer is WebAssembly. Compiling Rust to Wasm has a structural advantage: a Go program compiled to Wasm has to bring Go's garbage collector along, while Rust's ownership model makes memory interaction between the WebAssembly guest and the host straightforward. Cloudflare Workers have supported Wasm for a long time, and the platform recently added native Rust support through a new Emscripten target for wasm-bindgen. When build and deploy systems run in tight loops on Workers, a small memory footprint pays off immediately: a complete, sandboxed development toolchain can run where it could not run before, and can safely run client-side in a browser, on an embedded device, or in an IoT application.

// The Rust + WebAssembly path is the interesting part.
// Go compiled to Wasm drags its garbage collector along; Rust does not,
// which keeps guest/host memory interaction simple.
import { createChecker } from "@tsrs/wasm";

const checker = await createChecker({
  // point at the same tsconfig your build uses
  projectPath: "/tsconfig.json",
});

const diagnostics = await checker.check();
const errors = diagnostics.filter((d) => d.category === 1);

console.log(`errors=${errors.length}`);
for (const e of errors.slice(0, 5)) {
  console.log(`${e.file}:${e.line}  TS${e.code}  ${e.message}`);
}

6. What to Do Right Now

Two suggestions. First, treat it as a shadow checker: keep tsc as the authority in CI, run tsrs with continue-on-error, and record type-check duration as a metric. After a few weeks of paired data, decide whether to switch. Second, watch two things in flight. tsrs is being merged with Theo Browne's tsc-rs into a single package that aims to cover all of TypeScript's features, including the Language Server Protocol and content mappers; until that settles, editor experience will not be equivalent, so do not migrate your team's IDE setup yet. And Cloudflare says it will experiment with deeper integration, such as type-aware lint rules in Oxlint. If that works, checking types while linting may beat running tsc as a separate pass. For context, Cloudflare already hosts Vite, Rolldown, Oxlint and Oxfmt, and more than 10 million developers build on the platform. This is not a weekend experiment.

Benchmark charts and performance measurements

Same worker count, same config: that is the only comparison that counts

📌 Frequently Asked Questions

Is tsrs a fork of TypeScript?

No. Cloudflare states explicitly that it is not forking TypeScript, and that the TypeScript team remains solely responsible for the language's design and direction. tsrs aims to be a faithful port that preserves semantics strictly from the original implementation, which means you should evaluate it as a faster implementation rather than expect new language features.

How was the 8x number measured?

Against TypeScript 7.0 with the same number of type check workers. Cloudflare says the benchmark configuration was confirmed with the TypeScript team and that the harness is public in the oxc-project/tsrs repository under bench. Note that TypeScript 7.1 beta is itself up to 2.5x faster than 7.0, and some of those gains were upstreamed from tsrs.

Can I replace tsc today?

You can try, but do not make it the only source of truth immediately. Install tsrs, run it with --noEmit, then sort and diff its diagnostics against tsc's. If they match, you have evidence it is only faster. In CI, run tsrs with continue-on-error for a few weeks before switching the blocking job.

Why choose Rust instead of Go?

Because of WebAssembly. A Go program compiled to Wasm has to compile Go's garbage collector along with it, while Rust's memory management model makes guest and host memory interaction in WebAssembly straightforward. That lets the same toolchain run in server sandboxes, in the browser, on embedded devices, and in IoT applications.

Will my editor's language server get faster too?

Not automatically. tsrs is being merged with Theo Browne's tsc-rs into a single package intended to support all TypeScript features including the Language Server Protocol and content mappers. Until that work stabilises, editor type feedback will not be equivalent, so keep your team's IDE configuration on the official tooling.