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三个层级,层级就是承诺
scriptc 逐个构造地判断哪些能编译为原生代码,并且会告诉你结果。程序里的每个构造恰好落在下面一个层级。
静态编译
默认层级
原生代码,没有引擎。你写的那份 TypeScript 直接过真正的 TypeScript 编译器做类型检查,再落到原生代码上。stdout、stderr 与退出码和 Node 下逐字节一致,分歧只有一份带编号的记录清单。
约 320KB · 除系统 C 库外不链接任何东西
动态运行
--dynamic
内嵌 quickjs-ng 引擎,只为无法静态化的部分工作:npm 依赖自带的 JS,以及 any 类型代码。每个回穿进静态代码的值都在运行时校验,说谎的类型抛出可捕获的 TypeError,而不是破坏内存。
内嵌引擎约 620KB · 静态仍是默认
拒绝编译
编译期失败
其余一切在编译期停下,附带具体的错误码、一段代码帧,通常还有一个改写提示。没有任何代码会被静默地编译错误。
例如 SC2013 · SC3002
覆盖率报告 会让你程序里的层级分布一目了然。
编译产物可以逐段停下查看
用 --emit 停在任意一段,不同类型的产物累积在 .scriptc/ 目录里。
- TypeScript解析与类型检查
- 带类型的 IRhello.ir.json
- C可读的 C 代码
- LLVM IRhello.ll
- 原生 / WASMhello · hello.wasm
$ scriptc build hello.ts --emit=c >/dev/null
$ ls .scriptc/
hello.c
hello.ir.json
$ scriptc build hello.ts --emit=llvm >/dev/null
$ ls .scriptc/
hello.c
hello.ir.json
hello.ll
正确性由两个门禁保证
- 差分测试
- 语料库里每个程序既在 Node 下运行,也作为原生二进制运行,stdout、stderr 与退出码必须逐字节一致。服务器用实时客户端分别驱动两种实现。
- 内存安全通道
- 整个语料库在 AddressSanitizer 下重跑,并做一次引用计数审计。内存泄漏和释放后使用都算构建失败。
工作原理 说明了这条管线背后的架构。
全部文档,11 页
指南
装上就能试
编译器需要 Node.js 24 或更高版本。源码级产物(ir、c、llvm)只要 Node 就能生成,它产出的可执行文件不依赖 Node。
$ npm install -g scriptc
$ scriptc run hello.ts
hello, world