ts-r-run

Run TypeScript with Node. Replaces tsx, ts-node.

  • Pre-alpha
  • Works from source

ts-r-run compiles the files an entry imports, caches them, and starts Node with a small preload that serves the compiled code. It replaces tsx and ts-node for scripts, servers and tests. Like them, it doesn’t check types.

Run a 1,000-module programShorter is faster. Pre-release build, real app.
  • ts-r-run, warm cache101 ms
  • tsx 4.23270 ms

2.7× faster runs

What switching looks like

Nothing is on npm yet. Once it is, switching looks like this: ts-r-run reads the config you already have, so the change is to the command, not the project.

package.json
  "scripts": {
-   "seed": "tsx scripts/seed.ts",
+   "seed": "ts-r-run scripts/seed.ts",
-   "serve": "tsx watch src/server.ts",
+   "serve": "ts-r-run --watch src/server.ts",
  }

What works, and what doesn’t yet

Works today

  • .ts, .tsx, .mts and .cts entries, with ES modules and CommonJS in the same graph
  • Enums, namespaces, parameter properties and TC39 decorators
  • tsconfig paths and JSX settings; stack traces mapped back to TypeScript
  • --watch, which restarts Node when a file in the graph changes
  • A warm start that only checks file sizes and times before handing over to Node
  • Emit that matches tsc 7 on every file of the reference app

Not yet

  • The cache check doesn’t notice a new file that would change a resolution
  • Only the entry’s tsconfig.json is used for every file, as with tsx
  • Windows is untested

Measured

Every run, on the same reference app as the home page, with the command and commit that recorded it.

ts-r-run: running a TypeScript file

Time from starting the command to the program exiting, against tsx and Node's own type stripping. node -e 0 is the floor: starting Node with no TypeScript at all.

Run hello.tsShorter is faster, in ms
  • ts-r
  • Other tools
  • Baseline
  • node -e 0 (floor)23 ms
  • ts-r-run, warm cache29 ms
  • ts-r-run, empty cache32 ms
  • node --experimental-strip-types50 ms
  • tsx 4.2379 ms
Run a 1,000-module programShorter is faster, in ms
  • ts-r
  • Other tools
  • Baseline
  • node -e 0 (floor)24 ms
  • ts-r-run, warm cache101 ms
  • ts-r-run, empty cache137 ms
  • node --experimental-strip-types215 ms
  • tsx 4.23270 ms
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

Correctness

Compared with the tool ts-r-run replaces, on its own tests and on real code.

  • 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.

Command line

An excerpt of ts-r-run --help. You can also run it through the launcher as ts-r run.

Next to tsx and Node

ts-r-run needs nothing installed in the project and runs on the Node you already have. Unlike Node’s own type stripping, it handles enums, namespaces, parameter properties, decorators and tsconfig paths. Package imports stay with Node, so exports and runtime conditions behave exactly as they do without ts-r.

ts-r-run --help
ts-r-run: run a TypeScript file with Node

Usage: ts-r-run [options] [node options] <file> [args...]

  --watch               Restart when a file of the module graph changes
  --no-cache            Compile everything again
  --no-source-maps      Faster start on large graphs

Needs Node 22.15+ (module.registerHooks).