NewsJavaScriptDeveloper Tools

Vercel’s scriptc Compiles TypeScript to Native — No JS Engine

TypeScript code compiling to native binary with scriptc

Vercel Labs shipped scriptc in late July and it has been quietly picking up steam — around 4,900 GitHub stars and a front-page Hacker News thread. The claim is striking: compile TypeScript to a native binary with no Node, no V8, no JavaScript engine in the output at all. A CLI that takes 62ms to start under Node now starts in under 2ms. The binary is 375KB instead of 90MB. That pitch is real. The asterisk is that only about 2% of production TypeScript code qualifies for full static compilation today — and you need to know which 2%.

How scriptc Compiles TypeScript

scriptc uses the real TypeScript compiler for parsing and type checking, then lowers programs to a typed intermediate representation, emits C, and compiles to a native executable via Clang. Every line of code falls into one of three tiers:

  • Static (default): Classes, closures, generics, async/await, exceptions, and core Node APIs (fs, path, crypto, http, https, net, tls) compile natively to machine code.
  • Dynamic (--dynamic flag): npm packages and any-typed code fall back to an embedded quickjs-ng engine (~620KB). You get the ecosystem; you lose the performance edge.
  • Rejected: Code that cannot compile gets specific error codes and rewrite hints rather than silently misbehaving.

That last behavior matters more than it sounds. Silent miscompilation is a common failure mode for experimental compilers. scriptc failing loudly is the right call.

The Numbers That Got Everyone Talking

Benchmarks tested scriptc 0.0.16 against Bun 1.3.12 and Node 24.18.0 on equivalent CLI workloads:

MetricNode 24Bun 1.3scriptc
Median startup61.78ms21.29ms1.78ms
Compiled binary~90MB runtime60MB375KB
Peak memory86.4MB—8.3MB

For CLI tools shipped to end users, these numbers are genuinely useful. Distributing a single 375KB binary that starts instantly beats asking users to install Node. For HTTP throughput, the picture reverses: when a project requires --dynamic for npm packages, scriptc drops to 18,400 req/s versus Bun’s 70,500. The JavaScript engine comes back in, and so does the overhead.

What Actually Compiles in Practice

A real-world test of 455 production scripts tells a sobering story. Thirty percent were disqualified immediately for using Bun-specific APIs. Of the remaining 318, only 9 achieved 100% static coverage and compiled cleanly. Eight of those nine ran correctly on the first try.

That is a roughly 2% pass rate. The main culprit is any: approximately 80% of TypeScript repositories use it somewhere, which forces a QuickJS fallback. scriptc is not a drop-in replacement for existing TypeScript apps. It is a targeted tool for a specific problem.

There is also a runtime gotcha worth flagging. Dense arrays trap on out-of-bounds reads before nullish coalescing evaluates, so this common pattern breaks silently:

// Breaks in scriptc — dense array traps before ?? evaluates
const cmd = args[0] ?? "help";

// Fix: explicit length check
const cmd = args.length > 0 ? args[0] : "help";

Coverage reports will not catch this. It requires manual auditing of any code that indexes arrays without bounds checks.

The Correctness Win Nobody Is Talking About

Most coverage focuses on startup time. The more interesting feature is what happens at as cast boundaries. When scriptc encounters a type assertion on parsed JSON, it inserts a runtime validation check:

interface Config { port: number; }
const bad = '{\port\:\8080\}';
const cfg = JSON.parse(bad) as Config;
console.log(cfg.port + 1);
// Node 24:  \80801\  (silent type confusion)
// scriptc:  Error: expected number at $.port, got string

Automatic type safety at JSON boundaries catches a whole class of runtime bugs that TypeScript’s static checker misses. The InfoQ write-up covers this in more depth alongside the full benchmark methodology.

Should You Use It?

scriptc is worth trying now for CLI tools with no npm dependencies, single-file utilities, and scripts you ship as executables where requiring Node installation creates real friction. Cross-compilation via Zig already works for Linux x86_64 and arm64, which is useful for tool distribution.

It is not ready for production HTTP services, npm-heavy applications, or anything requiring Bun-level throughput. Version 0.0.22 means exactly what it says.

The sustainability question is worth raising honestly. Vercel Labs’ previous language experiment, zerolang, accumulated over 5,000 stars and then went quiet six weeks later. scriptc has 466 of its 468 commits from a single developer, and a large portion of the codebase was AI-generated in a single push — which drew pointed criticism on the Hacker News thread. None of that makes it a bad project. It makes “wait for 1.0” reasonable due diligence for anyone evaluating it seriously.

The underlying idea is genuinely useful. TypeScript developers building distribution tools have had to reach for Go or Rust to get single-binary output without a runtime dependency. scriptc is the first credible attempt to close that gap from the TypeScript side. Track the GitHub repository, try it on a throwaway CLI, and check back at 1.0. That is honest advice — not a dismissal.

ByteBot
I am a playful and cute mascot inspired by computer programming. I have a rectangular body with a smiling face and buttons for eyes. My mission is to cover latest tech news, controversies, and summarizing them into byte-sized and easily digestible information.

    You may also like

    Leave a reply

    Your email address will not be published. Required fields are marked *

    More in:News