• Written from scratch in Rust
  • Open source

TypeScript tooling, rebuilt in Rust.

Six small, fast tools that each replace one you use today: ts-r-dev, ts-r-build, ts-r-check, ts-r-lint, ts-r-run and ts-r-lsp. Each one reads the config you already have and works on its own, so you can switch one at a time.

Save to page updated, p50Shorter is faster. Measured on a real app.
  • ts-r-dev8.6 ms
  • Vite 8.3.435 ms

4.1× faster hot reload

Measured on a real app, against the tools you use today

Early numbers from pre-release builds on our reference app, a real TypeScript monorepo of 1,983 modules. Every number says how it was measured: open “How we measured” on any tile.

ts-r-dev Measuredreplaces the Vite dev server

4.1× faster hot reload

  • ts-r-dev8.6 ms
  • Vite 8.3.435 ms

Save to page updated, p50

How we measured
Measured
Headless Chrome (a fresh one per page load) drives both servers: 5 cold and 5 warm starts (median), then 50 measured edits of the welcome-screen heading after 3 warm-ups, timed in the browser. RSS is the whole server process group.
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-dev release build; Vite 8.3.4 with @vitejs/plugin-react 6.1.2 pinned in bench/package.json; both servers capped at 4 threads
Input
excalidraw at the commit pinned in bench/repos.toml, with the harness in bench/dev/excalidraw (index.html, main.tsx rendering excalidraw-app/App, vite.config.mjs with its source aliases)
Command
cargo xtask bench --fetch-repos --repos excalidraw && scripts/limit.sh --mem-mb 1024 --time-s 1800 -- node bench/dev/dev.mjs --app excalidraw --server both --mode bench
Recorded in
commit 9c3a371

ts-r-build Measuredreplaces esbuild, Rollup, Vite build

2.5× faster rebuilds

  • ts-r-build1,358 ms
  • esbuild (context.rebuild)3,457 ms

excalidraw: first build plus 20 rebuilds

How we measured
Measured
Wall time of whole processes with hyperfine (cargo xtask bench --repos), median; peak RSS of one extra run (--memory). Rebuilds: examples/build_loop.rs --edit against bench/js/esbuild-rebuild.mjs, editing excalidraw-app/components/AppWelcomeScreen.tsx
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-build release build with 4 threads; esbuild 0.28.2 pinned in bench/package.json
Input
excalidraw (bench/dev/excalidraw/main.tsx, .woff2 as files, .scss empty) and three.js x10 at the commits pinned in bench/repos.toml
Command
cargo xtask bench --fetch-repos --repos excalidraw,three-x10 && cargo xtask bench --repos excalidraw,three-x10 --memory
Recorded in
commit 9c3a371

ts-r-check Measuredreplaces tsc

1.6× faster type checks

  • ts-r-check1,936 ms
  • tsc 7.0.23,044 ms

Check all five projects, total wall time

How we measured
Measured
Wall time with hyperfine (cargo xtask bench --repos), median of 5 runs; peak RSS of one extra run. Every tool checks the same wrapper tsconfig (noEmit, not incremental)
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-check release build; TypeScript 7.0.2 (native tsc) pinned in bench/package.json; each repo's own TypeScript
Input
tRPC, date-fns, RxJS, TypeORM and Playwright at the commits pinned in bench/repos.toml
Command
cargo xtask bench --fetch-repos --repos trpc,date-fns,rxjs,typeorm,playwright && cargo xtask bench --repos trpc,date-fns,rxjs,typeorm,playwright --memory check-
Recorded in
commit 9c3a371

ts-r-lint Measuredreplaces ESLint + typescript-eslint

8.6× faster linting

  • ts-r-lint1,721 ms
  • ESLint14,725 ms

Three projects with ESLint configs, total

How we measured
Measured
Wall time of the whole command with hyperfine (cargo xtask bench --repos), median of 3 to 5 runs; peak RSS of one extra run
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-lint release build with 4 threads; each repo's own ESLint or oxlint from its lockfile
Input
tRPC, TypeORM, Playwright and date-fns at the commits pinned in bench/repos.toml, with their own lint configs
Command
cargo xtask bench --fetch-repos --repos trpc,typeorm,playwright,date-fns && cargo xtask bench --repos trpc,typeorm,playwright,date-fns --memory lint-
Recorded in
commit 9c3a371

ts-r-run Measuredreplaces tsx, ts-node

2.7× faster runs

  • ts-r-run, warm cache101 ms
  • tsx 4.23270 ms

Run a 1,000-module program

How we measured
Measured
Wall time, median of 15 runs
Machine
Apple M-series Mac, macOS arm64, release build (the exact chip was not recorded)
Software
Node 24.21.0; tsx 4.23 run as its binary (npx tsx adds about 400 ms); ts-r-run with 4 compile threads and source maps on
Input
A generated project of 1,000 TypeScript modules (interfaces, classes, typed functions, .ts imports so Node type stripping can run it too), and hello.ts
Command
cargo build --release -p ts_r_run --bin ts-r-run --example bench && TS_R_RUN_THREADS=4 scripts/limit.sh -- target/release/examples/bench --runs 15
Recorded in
commit e92e486

ts-r-lsp Measuredreplaces tsserver and the ESLint server

3.6× faster first errors

  • ts-r-lsp224 ms
  • tsserver800 ms

tRPC router.ts: first diagnostics

How we measured
Measured
A scripted LSP client: first diagnostics, hover, definition and completion latency (20 sampled positions), keystroke to diagnostics (30 keys, 150 ms apart) on one file, then memory with that file open and with every source of the project open (5 s settle); one run per tool
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-lsp release build with the ts-r backend and the tsc parity check off; each repo's own tsserver; TypeScript 7.0.2's native LSP (tsc --lsp) pinned in bench/package.json
Input
date-fns, RxJS and tRPC at the commits pinned in bench/repos.toml; the files and source directories are each repo's [repo.editor] set
Command
cargo xtask bench --fetch-repos --repos date-fns,rxjs,trpc && cargo xtask bench --editor --repos date-fns,rxjs,trpc (prints the commands; the editor-latency.js ones were run)
Recorded in
commit 9c3a371
What we haven’t measured yet
  • ts-r-build against Rolldown and Vite's production build.
  • ts-r-check against ts-rust, and on the 60-repository corpus the plan describes.
  • ts-r-lint against Biome.
  • ts-r-dev against Vite 8 on the generated 1,000 to 50,000-module apps.
  • The large public repos in bench/repos.toml (VS Code, Sentry, Grafana, Supabase, Backstage, n8n, cal.com, Next.js, React): they need a dedicated machine.
  • The VS Code editor-memory runs (stock VS Code against the ts-r extension) on the public repos.
  • Linux and Windows runs, and runs on a dedicated benchmark machine.

Not ready yet. Nothing is on npm yet. The type checker doesn’t give the same errors as tsc on every project, so keep tsc in CI until it does.

One engine under six tools

Every tool parses with the same front end, resolves modules the same way and shares one cache, so work done by one is reused by the next. Nothing underneath is borrowed: no swc, no oxc, no esbuild, no TypeScript. Pick a tool to see what it’s built on.

Show the crates a tool is built on

Front end

  • ts_r_lexerTS, TSX and JS tokens
  • ts_r_parserError-recovering parser
  • ts_r_astCompact, index-based AST

Project

  • ts_r_configtsconfig.json and extends
  • ts_r_resolverNode and TS resolution
  • ts_r_cacheContent-addressed disk cache

Types

  • ts_r_binderScopes and symbols
  • ts_r_checkerTypes, inference, diagnostics

Output

  • ts_r_transformStrips types, lowers JSX
  • ts_r_codegenJavaScript and source maps
  • ts_r_bundlerGraph, tree shaking, chunks
  • ts_r_minifyShorter names and syntax

Services

  • ts_r_hmrHTTP, WebSocket, hot updates
  • ts_r_lintRules and ESLint configs

14 shared crates, each with its own tests and notes, behind six separate binaries.ts-r-dev is built on 10 of the 14. How ts-r-dev worksts-r-build is built on 10 of the 14. How ts-r-build worksts-r-check is built on 8 of the 14. How ts-r-check worksts-r-lint is built on 6 of the 14. How ts-r-lint worksts-r-run is built on 8 of the 14. How ts-r-run worksts-r-lsp is built on 9 of the 14. How ts-r-lsp works

Six tools, each replacing one you use today

Each reads the config you already have and works on its own, so you can switch one at a time.

Hot reload stays flat as the app grows

ts-r-dev transforms only the file you saved and pushes that one update to the browser, so a save costs about the same at 50,000 modules as at 1,000. The plan’s target is under 30 ms.

Save to browser updated, p50, as the app growsLower is faster, in ms. Generated apps from 1,000 to 50,000 modules.
Target: under 30 ms
  • 4.9 ms at 1,000 modules
  • 4.8–5.0 ms at 10,000 modules
  • 5.4 ms at 50,000 modules

Checked against the tools it replaces

Speed only counts if the answers are right. Each part runs the reference tool’s own tests, or compares output on real code, and the gaps are published with the wins.

  • Module resolution100%

    63,958 of 63,958 resolutions match (0 differences)

    Every resolution in tsc --traceResolution for 24 tsconfig files of private-app. Commit 856e43d.

  • tsconfig loading100%

    49 of 49 configs match tsc --showConfig exactly

    private-app tsconfig files. Commit 856e43d.

  • JavaScript output (emit)100%

    339 of 339 files match

    private-app apps and packages, 15 tsconfig files. Commit e92e486.

  • JavaScript output (emit)91.0%

    1,611 match, 159 differ

    tsc 7's own emit baselines (tests below ES2022, outFile, AMD/System/UMD and files with syntax errors skipped). Commit b46cb28.

  • Parse, print, parse again100%

    18,079 match, 0 differ

    TypeScript's tests/cases (1,133 files with syntax errors skipped). Commit f3c5476.

  • Binder diagnostics90.0%

    398 of 442 tests match exactly; 1,683 of 1,789 expected diagnostics found

    tsc 7.0.2 baselines for the binder's error codes. Commit 16a6b7e.

  • Type checker58.0%

    58.0% of 13,398 conformance test variants exact; 937 variants with a diagnostic tsc does not report

    tsc 7.0.2 conformance baselines. Commit aaa5051.

  • Bundler86.1%

    643 of 746 compared cases pass (86.2%)

    esbuild 0.28.2's end-to-end bundler tests. Commit 7db7af5.

  • Lint rules100%

    8,597 of 8,597 cases match

    Every case of the published ESLint and typescript-eslint tests for the 103 rules. Commit 27c280e.

Packages passing each stageLonger is better
  • Parse every file99.9%
  • Bundle and run (vs esbuild)92.2%
  • Transform TypeScript (vs tsc)87.5%
  • Check an import of it (vs tsc 7)77.6%
  • Own test suite0%

The top 1,000 npm packages, each stage compared with tsc 7 or esbuild. No package’s own test suite passes with ts-r swapped in yet.

How it’s built

  • Written from scratch

    The lexer, parser, binder, checker, bundler, minifier, linter, dev server and language server are all written in this repo, in Rust, with no other compiler underneath.

  • Other tools are the oracle

    TypeScript’s conformance tests, esbuild’s bundler tests and ESLint’s rule tests run against ts-r. Where ts-r disagrees, ts-r is wrong until shown otherwise.

  • Every number has a commit

    Each benchmark records its machine, input, command and the commit that measured it. Numbers that miss their targets are published too.

  • One tool at a time

    Each tool is its own binary that reads your existing config. Adopt the linter this week and keep tsc, Vite and ESLint for everything else.

The repo’s own checks
# TypeScript’s tests, libs and diagnostics, pinned
$ cargo xtask upstream sync
# tsc 7 as the reference for every diagnostic
$ cargo xtask oracle
# against tsc 7, esbuild, oxc and tsx; 5% regression gate
$ cargo xtask bench
# the top 1,000 npm packages, stage by stage
$ cargo xtask ecosystem

Numbers on this site were last updated on 16 October 2026.

Frequently asked questions

What is ts-r?

Six command-line tools for TypeScript, written from scratch in Rust: a dev server, a bundler, a type checker, a linter, a runner and a language server. Each one replaces one tool you use today.

Do I have to switch everything at once?

No. Each tool works on its own next to tsc, ESLint and Vite, and reads the config you already have: tsconfig.json, your ESLint config, your vite.config. Switch one at a time, in any order.

Is it a fork of tsc, esbuild or oxc?

No. Every layer (lexer, parser, type checker, bundler, minifier, linter) is written in this project. Other tools’ test suites and outputs are used to check that ts-r gives the same answers.

Will it be free?

Yes. ts-r will be open source under MIT OR Apache-2.0, with no paid tier. Sponsorships help fund development time.

When can I use it?

It runs from source today and isn’t on npm yet. The type checker doesn’t give the same errors as tsc on every project yet, so keep tsc in CI until it does.