macOS · Linux · Windows

TypeScript-to-Native Compiler

Ordinary TypeScript becomes a small, fast native binary — no Node, no V8, no JavaScript engine required. What compiles behaves byte-for-byte like Node.

fib.ts — compiled with scriptc
$ cat fib.ts
function fib(n: number): number {
  return n < 2 ? n : fib(n - 1) + fib(n - 2);
}
console.log(fib(30));

$ scriptc run fib.ts
832040

$ scriptc build fib.ts -o fib && ./fib
832040

Three tiers, always explicit

Every construct in your program lands in exactly one tier, and the tier is the promise.

Tier 1

Compiled statically

The default. Ordinary TypeScript — classes, closures, async/await, the stdlib, Node's fs/path/process/http surface — becomes native code with no engine in the binary. What compiles behaves byte-for-byte like Node.

Tier 2

Runs dynamically

Opt in with --dynamic: an embedded JavaScript engine (~620KB) executes what can't be static — npm dependencies' shipped JS, any-typed code. Every value crossing back into static code is validated at runtime.

Tier 3

Rejected at compile time

Everything else fails the build with a specific error code, a code frame, and usually a rewrite hint. Nothing is ever silently miscompiled.

Staticness you can see

Most TypeScript is far more static than the ecosystem assumes. scriptc decides, construct by construct, what can compile to native code — and tells you. A binary never silently grows an engine: the dynamic tier is opt-in, and the coverage report names every site that needs it.

Reading coverage reports →
$ scriptc coverage cli.ts

  statements analyzed   4
  compile statically    3  (75%)

  runs with --dynamic   2 sites (embeds a JS engine, ~620KB — static stays the default)
      ×1  importing 'picocolors' requires the embedded dynamic engine — the package's implementation runs there  SC2013

No code changes

No annotations, no dialect, no special stdlib. The same TypeScript you run on Node, type-checked by the real TypeScript compiler.

Small and fast

A hello-world binary is ~320KB, starts in about 4ms, and links against nothing but libSystem. Node needs a ~120MB runtime and ~35ms to print the same line.

Measured coverage

scriptc coverage tells you, statement by statement, what compiles statically, what needs the dynamic engine, and exactly what blocks the rest.

Differentially tested

Every corpus program runs under Node and as a native binary; stdout, stderr, and exit codes must match byte-for-byte. The whole corpus re-runs under AddressSanitizer.

Compile your first binary

Clone the repository, build the compiler, and turn a TypeScript file into a native executable in a couple of minutes.