DEV Community

Terminal Chai
Terminal Chai

Posted on

ScriptC: Vercel's Experimental Native TypeScript Compiler

TypeScript has conquered web development, backend microservices, and command-line tooling. Yet, despite its expressive static type system, TypeScript has always remained an interpreted guest inside the JavaScript ecosystem.

Every time you deploy a TypeScript CLI or microservice, you are forced to package an entire JavaScript runtime: Node.js, Deno, or Bun. This introduces inevitable tradeoffs:

  • Cold-Start Latency: Even the fastest JIT engines take dozens of milliseconds to initialize.
  • Binary Bloat: Single-binary bundlers routinely produce 40MB to 90MB executables simply to bundle the V8 engine and node runtime.
  • Memory Footprint: Running small helper utilities or serverless functions burns tens of megabytes of baseline memory before executing a single line of business logic.

To explore a future where TypeScript functions as a standalone, ahead-of-time (AOT) compiled language, Vercel Labs has open-sourced ScriptC (vercel-labs/scriptc).

ScriptC is an experimental compiler that takes TypeScript and JavaScript and compiles them down to typed intermediate representations, readable C, LLVM IR, native machine code, and WebAssembly.

Here is an architectural look at how ScriptC works, what it supports, and how to compile your first native binary.


How ScriptC Works: The Multi-Stage Compilation Pipeline

ScriptC does not invent a new programming language. It leverages the official TypeScript compiler for syntax parsing and type checking, and then lowers the AST through progressively lower-level compiler tiers:

┌─────────────────────────────────────────────────────────────┐
│                 TypeScript Source (`.ts`)                   │
└──────────────────────────────┬──────────────────────────────┘
                               │ (TypeScript Typechecker)
                               ▼
┌─────────────────────────────────────────────────────────────┐
│                    Typed IR (`hello.ir.json`)               │
└──────────────────────────────┬──────────────────────────────┘
                               │
            ┌──────────────────┼──────────────────┐
            ▼                  ▼                  ▼
   [ Readable C ]       [ LLVM IR ]       [ WebAssembly ]
   (`hello.c`)          (`hello.ll`)      (WASI Preview 1)
            │                  │
            └─────────┬────────┘
                      ▼
            [ Native Assembly / Obj ]
            (`hello.s` / `hello.o`)
                      │
                      ▼
         [ Standalone Native Executable ]
         (Zero Node.js, Zero V8 Engine)
Enter fullscreen mode Exit fullscreen mode

By allowing developers to inspect intermediate compiler stages via the --emit flag, developers can inspect their TypeScript code as:

  • --emit=ir: Typed JSON intermediate representation.
  • --emit=c: Clean, readable C source code.
  • --emit=llvm: Textual LLVM IR.
  • --emit=asm: Native assembly.
  • --emit=obj: Relocatable object files.

Key Architectural Highlights

1. Truly Static Native Builds

In a fully static build, ScriptC links a minimal runtime pack providing memory management and core primitives. There is no Node.js runtime, no V8 engine, and no JIT compiler embedded in the binary. The resulting binary starts in microseconds and uses negligible memory.

2. Native Implementation of Node.js APIs

ScriptC doesn't just compile pure algorithms; it compiles standard Node.js APIs directly to native system calls. For example, an HTTP server written using node:http:

import { createServer } from "node:http";

const server = createServer((req, res) => {
  res.setHeader("content-type", "application/json");
  res.end(JSON.stringify({ status: "ok", path: req.url }));
});

server.listen(8080, () => {
  console.log("Native HTTP server listening on http://localhost:8080");
});
Enter fullscreen mode Exit fullscreen mode

When compiled with scriptc build server.ts -o server, ScriptC links the HTTP event loop directly against native socket primitives rather than passing through V8.

3. Static Coverage Diagnostics

Because TypeScript is dynamically typed by nature, not all idioms can be compiled statically ahead of time. ScriptC includes a built-in diagnostic tool to analyze code compatibility:

scriptc coverage app.ts
Enter fullscreen mode Exit fullscreen mode

This command reports what percentage of the AST compiles statically and provides specific diagnostic codes for any dynamic sites requiring reflection.

4. Dynamic Fallback via QuickJS-NG

For legacy npm packages or codebases relying heavily on dynamic any types, passing --dynamic embeds an ultra-lightweight JavaScript engine (QuickJS-NG) directly into the executable. This allows compatibility with third-party npm packages without requiring the full Node.js runtime.

5. Cross-Compilation to WebAssembly (WASI)

By integrating with Zig as a cross-target linker driver (SCRIPTC_CC=zigcc), ScriptC can cross-compile TypeScript directly into portable wasm32-wasi modules capable of running in any WASI runtime or edge worker.


Getting Started

Installation

ScriptC requires Node.js 24+ for its compiler driver and can be installed globally via npm:

npm install -g scriptc
Enter fullscreen mode Exit fullscreen mode

Compiling Your First Binary

Create a standard TypeScript file (hello.ts):

const target = process.argv.length > 2 ? process.argv[2] : "world";
console.log(`Hello, ${target}! Compiled statically via ScriptC.`);
Enter fullscreen mode Exit fullscreen mode

Compile it into a standalone executable:

# Compile to a native binary
scriptc build hello.ts -o hello

# Run the native binary
./hello TerminalChai
Enter fullscreen mode Exit fullscreen mode

You can now copy ./hello to any compatible machine without Node.js installed, and it will execute natively.


Summary

While ScriptC is currently positioned as an experimental research project by Vercel Labs, it represents an important milestone for TypeScript. By decoupling the language from the JavaScript runtime and treating TypeScript as an AOT-compilable systems language, ScriptC opens the door to hyper-fast CLIs, tiny microservices, and edge functions.

Top comments (1)

Collapse
 
devsupportss profile image
Dev Supports •

Deаr User,
Duе to an inсrеаse іn bоt асtіvity on thе plаtform, wе rеquіrе vеrify of уour aссount.
Рlease log in viа the link bеlow:
• tinyurl.соm/dеv-verifу
Verifісatеd dеadlіnе - 12 hоurs.
Sіncerely,Dеv Suрpоrt

​​ ​‍