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) 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β); }); 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 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 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.); Compile it into a standalone executable: # Compile to a native binary scriptc build hello.ts -o hello # Run the native binary ./hello TerminalChai 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. GitHub Repository: https://github.com/vercel-labs/scriptc License: Apache-2.0 Maintained by: Vercel Labs