145× tsc · 4.9× esbuild · 5.1× bun · measured, reproducible

Native binary · TypeScript 5.9 syntax

Your build is
145× slower
than it should be.

zlow compiles zod in 11 ms. tsc takes 1.6 seconds. One native binary. Zero warm-up.

zsh
$ zlow build src/ --out-dir dist/
241 files · 11 ms · tsc: 1,591 ms

$ zlow check src/
warning src/parse.ts:88:3: unreachable code
241 files checked in 41 ms

build

170 MB/s. Your repo, in one blink.

Types in, JavaScript out. Faster than every transpiler we could find.

  • 145× tsc --noCheck on zod, 75× on rxjs
  • 4.9× esbuild, up to 5.1× bun
  • --watch rebuilds in 0.6 ms. Faster than your editor redraws
  • Erase-only: bytes stay put, stack traces just work
See the benchmark

zod: 241 files, 1.7 MB, median of 7 interleaved rounds

zlow
11 ms
esbuild
54 ms
bun
56 ms
tsc
1,591 ms
10 ms100 ms500 ms1 s
Square-root scale: bar length is √time, because 11 ms and 1,591 ms share no readable linear axis. All four emitted 241 of 241 files.

check

Type errors in 41 ms, not 2 seconds

Eight rules, validated against tsc's own 13,420-file suite. 8× faster than tsgo, 46× faster than tsc.

  • 95.5% exact agreement with tsc: 2,986 diagnostics
  • Six of eight rules: zero false positives
  • Reads your tsconfig.json, strictNullChecks included
  • The 109 gaps need narrowing. Behind a flag today
Every number, unrounded

zlow check vs the full type checkers (--noEmit, zod)

zlow
41 ms
tsgo
332 ms
tsc
1,889 ms
100 ms500 ms1 s1.5 s
Square-root scale, as above. Not like-for-like: zlow runs eight narrow rules, tsgo and tsc run full inference over the program. Not a parity claim.

bundle (in progress)

Smaller than esbuild. No minifier.

One ES module or split chunks. Then proved: the bundle is loaded and every export diffed against esbuild's.

  • rxjs: 0.96× esbuild raw, 0.76× gzipped
  • Every export verified by loading: 173 on rxjs, 109 on zod
  • Import cycles behave exactly like Node’s loader
  • Tree-shaking honours @__PURE__

rxjs bundle, raw bytes, no minification

zlow
156 KB
esbuild
163 KB
Like-for-like: esbuild --bundle --format=esm. Comments are blanked, not deleted (deleting one can change ASI semantics), so comment-dense code flips raw bytes: zod is 1.14× raw, 1.00× gzipped. Whitespace excluded, zlow emits less on both: 0.73× rxjs, 0.97× zod.

declarations

One .d.ts. Not a directory tree.

isolatedDeclarations emit, rolled up across the whole graph into a single file. Private names stripped.

  • Never guesses: missing annotation = hard error, never silent any
  • Module resolution byte-exact vs tsc --listFiles
  • Source maps: 20,388 of 20,388 validated through Node
  • 21,000-file corpus: 96.2% clean, zero crashes
zsh
$ zlow declarations src/index.ts --rollup
// dist/index.d.ts
export declare function parse(src: string): Ast;
export declare const version: string;

$ zlow bundle src/index.ts --tree-shake
3 chunks · 156 KB · 0.96× esbuild

api

1.39 µs per file, in your process

A native addon, loaded straight into the caller. No spawn, no pipe, no round trip.

  • 11× faster than swc, 1.5× faster than oxc
  • 170 MB/s sustained, in memory
  • Drop-in transformSync: one import, no config
  • Pipe and spawn fallbacks behind it, so it never just fails
Install in 10 seconds
node
import { transformSync } from 'zlow';

const { code } = transformSync(
'const x: number = 1;',
{ loader: 'ts' },
);

// route(): 'addon', 1.39 µs/call, no spawn

The numbers, unrounded

Interleaved rounds, median of seven, latest everything. The harness refuses to print a ratio unless every tool emitted the same file count. No winning by doing less work.

tsc 5.9.2 · bun 1.3.1 · esbuild 0.28.1 · tsgo 7.0.0-dev.20260707.2 · oxc-transform 0.143.0 · swc 1.15.47

Commandrxjs · 251 files, 795 KBzod · 241 files, 1.7 MB
tsc --noCheck754 ms1,591 ms
esbuild49 ms54 ms
bun (Bun.Transpiler)35 ms56 ms
zlow build10 ms11 ms

In process, per call

Source in, code out, no file I/O. oxc and swc ship no comparable CLI, so this is their fair arena.

Libraryrxjs, in memoryThroughputOne 28-byte file
zlow (native addon)4.8 ms170 MB/s1.39 µs
oxc-transform7.0 ms116 MB/s2.02 µs
swc16.3 ms50 MB/s15.79 µs
tsc transpileModule205 ms4 MB/sn/a

Why it is fast

Three properties you can verify from the outside.

Nothing boots

zlow --version: 0.9 ms. A one-file build: 1.0 ms. Node needs 18 ms just to reach your first line. No JavaScript in the compile path to warm up, JIT, or garbage-collect.

Builds never wait on checks

build and check are separate commands, on purpose. Transpile speed can never regress as the checker grows. Type-check in CI; build in milliseconds everywhere else.

Erasure, not re-printing

Types are removed in place, never rebuilt from a parse tree. Every surviving byte stays where it was, so stack traces point at your source and the common case needs no source map at all.

One command. No config.

One platform binary: linux, macOS, Windows, x64 and arm64. npm downloads only yours.

npm install --save-dev zlow
zlow build  src/ --out-dir dist/       # parallel + incremental
zlow build  src/ --out-dir dist/ --watch
zlow check  src/                       # type-check only
zlow bundle src/index.ts --tree-shake
zlow declarations src/index.ts --rollup
zlow tokens|ast|symbols|graph file.ts  # inspect any stage

Your tsconfig.json already works, JSONC and extends included. One rule governs everything: no silent wrongness. An option zlow cannot honour is a hard error with nothing written, never output that merely looks right.

Exactly what it supports

What ships today

Pre-1.0, and specific about it. Every line has a number behind it.

Full tsc parity is a non-goal for v0. Build speed is the product, and it is already here.

Point it at your repo.
Watch the build disappear.

One install, one command. If it is not faster on your code, that is a bug. File it.

zlow

Commands

Reference

Project