如果 TypeScript 能像 Go、Rust 一样,直接编译成一个不带 Node.js 的原生二进制,你会用吗?
Vercel Labs 最近开源的 scriptc ,想做的就是这件事。一个普通的 fib.ts ,执行 scriptc build fib.ts 后,可以得到约 178 KB 的独立可执行文件,官方给出的启动时间约为 2 毫秒。
![]()
项目很快冲上 GitHub 热门,但评论区也冒出一个尖锐质疑: 这不就是在抄 Perry 吗?
因为在 scriptc 出现之前,Perry 已经打出了几乎相同的口号,同样把 TypeScript 编译成不依赖 Node.js、V8 和 Electron 的原生程序。
Perry 确实更早,scriptc 也确实撞上了相同的产品叙事,但现有公开证据不足以支持“纯抄”这个结论。
scriptc 到底做了什么
scriptc 不是 pkg 、Node.js SEA 那类打包工具。
传统的单文件打包方案,通常会把 JavaScript 代码和 Node.js 运行时一起塞进可执行文件。你得到的是一个方便分发的文件,里面仍然带着 JavaScript 引擎。scriptc 的静态模式则尝试把 TypeScript 代码真正转换成原生机器码,最终产物不包含 Node.js、V8 或其他 JavaScript 引擎。
它的编译链路并不神秘:
使用 TypeScript 官方编译器完成解析和类型检查。
把代码转换成带类型的中间表示。
默认通过 LLVM 生成机器码,也保留 C 后端作为参考实现。
C 后端交给 Clang,最终链接成原生可执行文件。
真正困难的部分,不是把 number 变成机器指令,而是还原 JavaScript 的行为。
字符串要保持 UTF-16 语义,数组、Map、Set 要遵守 JavaScript 的顺序和身份规则,闭包、异常、 async/await 、事件循环也不能只做一个“差不多能跑”的版本。scriptc 为此实现了一套 C 运行时,并用差异测试反复比较同一段程序在 Node.js 和原生二进制中的输出、错误信息与退出码。
遇到无法静态编译的 npm 依赖或 any 代码时,开发者可以显式开启 --dynamic ,把 quickjs-ng 嵌入二进制。官方把处理结果分成三类:
静态编译 :不携带 JavaScript 引擎。
动态执行 :显式开启后,由嵌入式引擎处理不能静态转换的部分。
拒绝编译 :给出具体错误码和修改提示,不静默生成错误程序。
这个边界比“一切 TypeScript 都能直接变成机器码”诚实得多。
![]()
Perry 确实来得更早
质疑不是凭空出现的。
Perry 的 GitHub 仓库创建于 2026 年 1 月,首个公开提交也在 1 月。scriptc 的 npm 包最早出现在 7 月 13 日,GitHub 仓库则到 7 月 22 日才公开。按公开时间线计算,Perry 领先了大约半年。
两者的宣传语也很像:
都强调 TypeScript 直接编译到原生代码。
都强调产物不依赖 Node.js 和 V8。
都把小体积、低启动延迟和单文件分发作为卖点。
都在补齐常见 Node.js API 和 npm 包兼容能力。
所以,Perry 用户看到一家更有影响力的公司带着相似项目入场,心里不舒服完全可以理解。小团队先做出来,大公司后来拿走聚光灯,这在开源社区并不少见。
但“更晚做同一类产品”和“抄了代码”,不是一回事。
看代码,两者走的不是一条路
我对比了两个项目当前公开的架构和代码目录,最明显的区别有三处。
第一,编译器前端不同。
scriptc 直接调用 TypeScript 编译器 API,利用 tsc 的解析、类型检查和类型收窄结果,再生成自己的 typed IR。Perry 的主体以 Rust 编写,使用 SWC 解析 TypeScript,并维护自己的类型系统、HIR 和转换步骤。
第二,运行时策略不同。
scriptc 的静态运行时主要用 C 实现,内存管理采用引用计数并配合循环回收。动态模式嵌入 quickjs-ng。Perry 的编译器和运行时主体是 Rust,采用自己的 GC、标准库和大量原生扩展模块。
第三,产品边界不同。
scriptc 当前更像一个面向 CLI 和服务端程序的 TypeScript 原生编译实验,重点是 Node.js 行为一致性、静态覆盖率和可诊断性。Perry 已经把范围扩展到桌面、移动端、手表、电视、WebAssembly 和原生 UI 组件,野心明显更大。
两者都会经过“解析、类型表示、降低、代码生成、链接”这些阶段,也都使用 LLVM。可这是一类原生编译器常见的技术骨架,不能仅凭架构相似就认定抄袭。
目前能确认的是:
Perry 更早公开,应该得到先行者应有的关注。
两个仓库的主要实现语言、前端和运行时设计明显不同。
在没有代码相似性、提交来源或许可证违规证据之前,“纯抄”仍然是一句情绪化指控。
这场争论背后,其实是 TypeScript 生态正在发生的一次路线分化。
过去我们默认 TypeScript 最终还是 JavaScript。类型只服务于开发阶段,运行时继续交给 Node.js、Bun 或浏览器。scriptc 和 Perry 都在挑战这个前提:既然大量业务代码已经具备足够稳定的类型信息,为什么不能把其中一部分直接编译成机器码?
这条路如果走通,对命令行工具、Serverless 函数、小型服务和边缘任务很有吸引力。启动更快、内存更低、部署时不需要携带完整运行时,源码也不必作为 JavaScript 一起分发。
但别急着把 Node.js 卸了。
JavaScript 的动态能力、原生扩展、复杂 npm 依赖和 Node.js 行为兼容,都是长期工程。scriptc 目前版本仍是 0.0.x ,官方也把 macOS arm64 列为主要平台。Perry 虽然覆盖面更广,距离完整兼容整个 Node.js 生态同样很远。
我的看法是,scriptc 最值得欢迎的地方,不是它“发明”了 TypeScript 原生编译,而是 Vercel Labs 的入场把这条小众路线推到了更多开发者面前。
Perry 应该被看见,scriptc 也应该按自己的实现质量接受检验。可以质疑它为什么不提先行项目,可以比较谁的兼容性和性能更扎实,但在证据出现之前,没必要把两个开源项目的竞争压缩成一句“谁抄了谁”。
真正的胜负,最终还是要由真实项目、长期维护和兼容性测试决定。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.