TypeScript 也能编译成原生?

scriptc 把熟悉的 TypeScript 语法带到原生编译场景,但它不是 Node.js 的替代品:本文从类型、运行时、模块和调试四个问题出发,带你判断它适合什么项目。

源仓库: vercel-labs/scriptc

它为什么突然火了?

vercel-labs/scriptc 是一个尝试把 TypeScript 编译为原生可执行代码的实验性工具。它抓住了一个长期存在的痛点:开发者喜欢 TypeScript 的类型、语法和生态,却不希望每个小工具都依赖 Node.js、启动运行时并承担额外内存开销。GitHub Trending 上的关注,既来自“前端语言也能产出 native binary”的反差,也来自 AI coding agent 对轻量脚本、边缘任务和单文件工具的需求。不过要先校准预期:scriptc 并不是把完整 JavaScript 生态搬进 C++,而是通过受限语言子集、编译期分析和运行时支持换取可部署性。真正值得讨论的不是“它会不会取代 Node”,而是哪些 TypeScript 程序已经足够简单,可以接受这套边界。

1. 为什么普通 TypeScript 代码不能直接编译?

现象是最熟悉的代码一放进 scriptc 就出现不支持的语法、类型或 API。根因在于 TypeScript 的类型会被擦除,而 JavaScript 还拥有动态对象、原型链、反射、eval 等运行时行为;原生编译器必须在编译期知道更多信息。因此应把它当作“带 TypeScript 外观的可编译子集”,先从纯函数和明确类型开始,避免依赖 Node 全局对象。

export function sum(values: number[]): number {
  let total = 0;
  for (const value of values) total += value;
  return total;
}

console.log(sum([2, 3, 5]));

先用仓库提供的示例和测试验证支持范围,再逐步加入对象、字符串和错误处理;不要从一个已有的 Express 应用直接迁移。

2. npm 包为什么经常不能用?

很多人看到 TypeScript 就自然想到 npm install。但 npm 包通常假定存在 Node 的模块加载器、Buffer、文件系统、异步事件循环,甚至会在运行时生成代码。原生编译器即使能解析 import,也未必实现这些隐含契约。解决办法是把依赖分成三类:语言内置能力、scriptc 已适配的标准库、纯 TypeScript 源码。第三方包若使用动态特性,应改写为小型适配层;文件、网络和环境变量则集中放在边界模块,并确认文档中有对应运行时支持。

// 将业务逻辑与平台能力分开,便于替换实现
export type Clock = () => number;

export function isFresh(createdAt: number, now: Clock): boolean {
  return now() - createdAt < 60_000;
}

这样核心代码不依赖 process 或 Node API,迁移和测试都更可控。

3. 原生二进制一定更快吗?

不一定。编译成 native 通常能减少启动成本,并改善稳定的数值计算或循环;但如果程序主要等待网络、频繁分配对象,瓶颈仍在 I/O、序列化和算法。另一个误区是只看“编译成功”,却没有和 Node 版本做同一输入、同一机器的基准。正确做法是准备固定数据集,分别测冷启动、吞吐、峰值内存和二进制体积;同时检查优化是否改变了边界行为。对 CLI 和短生命周期任务,启动时间可能比峰值吞吐更有价值;对长时间服务,则应先分析 profile。

4. 出错时如何调试?

原生编译链会让错误从三层产生:TypeScript/语法诊断、编译器 lowering、生成程序运行时。若只看最终的退出码,定位会很慢。建议保留最小复现,先把输入缩到一个函数,再分别检查类型和平台 API;构建时开启仓库支持的调试输出,并在关键分支打印结构化信息。不要假设 JavaScript 的异常、堆栈和 source map 行为完全一致。对生产脚本,给每个外部输入增加显式校验,避免依赖隐式 coercion。

5. 它适合什么项目?

最合适的是计算明确、依赖少、输入输出简单的 CLI、构建辅助工具、边缘环境小函数和 AI agent 生成的可复现脚本。最不适合的是大型 Web 服务、重度 npm 应用、动态插件系统以及依赖完整 Node 兼容性的项目。可以采用渐进式策略:先把纯业务函数编译成 native,再让 Node 或其他宿主负责网络和编排;只有基准数据证明收益成立,才扩大范围。这样即使 scriptc 的 API 或支持矩阵变化,风险也被限制在边界内。

Sources