swc-node:用 SWC 替换 ts-node,免掉类型检查的运行时加速方案
更快的 ts-node,无需类型检查。详细信息:@swc-node/core 基准测试将 RxJS AjaxObservable.ts 转换为 ES2015 和 CommonJS JavaScript。
秒懂
- 它是什么?
- swc-node 是一组基于 SWC 的 TypeScript 运行时转换工具,目标是用更快的速度替代 ts-node 的 transpile-only 模式。本文拆解其核心包、注册器与 jest 集成的实际用法,并指出它在类型安全上的取舍。
- 适合谁用?
- swc-node 适合那些已经接受 transpile-only 工作流、并且被 ts-node 速度困扰的 TypeScript 项目,尤其是大型代码库的本地开发与 jest 测试。它不适合需要类型检查作为拦截关卡的项目,因为 register 和 jest 集成都不做类型诊断。
- 能商用吗?
- 可以。MIT 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
- 还在维护吗?
- 在维护。仓库最近一次提交在 3 天前。
- 用什么语言写的?
- 主要是 TypeScript(依据 GitHub 的语言统计)。
以上回答依据项目的 GitHub 数据(最近同步于 2026年9月14日)和我们的分析,不构成法律意见。
开源项目深度解析
它解决的是运行时转换的速度瓶颈
ts-node 在 Node 里直接执行 TypeScript 文件,但默认会做完整类型检查。类型检查本身很慢,而且对运行结果没有任何贡献。很多项目其实只需要把 TypeScript 转成 JavaScript 就跑,类型检查交给 IDE 或 CI。swc-node 正是瞄准这个场景,用 SWC 这个用 Rust 写的编译器来做转换。README 里明确写着它是 Faster ts-node without typecheck,也就是去掉类型检查,只保留转译。它的目标用户是那些被 ts-node 的 transpile-only 模式拖慢、又不想引入额外构建步骤的 Node 开发者。
从 @swc-node/core 到 register,分层清晰
仓库拆成多个包,核心是 @swc-node/core,它封装了 SWC 的转换接口。README 里给出了 transformSync 和并行 transform 的基准对比,用的样例是 RxJS 的 AjaxObservable.ts。单线程下 esbuild 是 510 ops/sec,@swc-node/core 是 438,typescript 只有 28.83,babel 是 24.21。并行模式下,设置 UV_THREADPOOL_SIZE=11 后,@swc-node/core 达到 1253 ops/sec,反而超过 esbuild 的 914。这说明它的并行设计在 CPU 多线程场景下能发挥优势。@swc-node/register 则是面向最终用户的入口,它模拟 ts-node 的 register 机制,通过 node 的 -r 参数钩住模块加载。
一条命令跑起来,但 ESM 路径有讲究
安装和启动都很直接。npm i -D @swc-node/register 之后,用 node -r @swc-node/register script.ts 就能运行 TypeScript 文件。这个用法和 ts-node 几乎一样,迁移成本很低。但 ESM 项目需要额外参数,README 区分了三个版本的 Node:Node 22.15 以上用 --import @swc-node/register/esm-register-next,Node 20.6 以上用 esm-register,Node 20.5 以下则用已废弃的 --loader。所有 ESM 路径都要求加上 --enable-source-maps,否则报错信息里的行号会指向生成的 JS。这个细节容易踩坑,建议直接照 README 的命令复制。
SWCRC 开关与 tsconfig 的边界
默认情况下,@swc-node/register 不会读取 .swcrc 文件,它只认 tsconfig.json 里的编译选项。如果你需要 SWC 的自定义配置,比如插件或特定的 transform 行为,必须设置环境变量 SWCRC=true。README 还提到,用 shebang 运行时可以加 TS_NODE_PROJECT=null 来忽略 tsconfig.json,例如 #!/usr/bin/env TS_NODE_PROJECT=null node --import @swc-node/register/esm-register。这种设计把配置来源分得很清楚:要么完全走 tsconfig,要么完全走 .swcrc,不能混用。对于有复杂 SWC 管线的项目,这个开关是必要的,但默认关闭也意味着很多人可能不知道它的存在。
jest 集成:测试速度提升最直观
@swc-node/jest 是给 jest 用的转换器,README 里有一组对比数据。在同一个纯 TypeScript 项目里,ts-jest 配置了 isolatedModules: true,跑 49 个测试套件,ts-jest 耗时 54.631 秒,@swc-node/jest 只用了 10.511 秒,整体时间从 62.71 秒降到 14.34 秒。这个差距接近 4 倍,对 TDD 循环来说体验差异非常大。不过要注意,这个基准是官方仓库自己跑的,硬件和项目规模都固定,实际项目里提升幅度会因代码而异。另外,isolatedModules 模式本身就跳过了类型检查,所以这个对比其实是转译器之间的较量,不是完整 ts-jest 的对比。
没有类型检查,这是优点也是陷阱
swc-node 的核心卖点是快,代价是零类型诊断。这意味着类型错误不会阻止代码运行,也不会在测试中暴露。比如一个函数参数类型写错,ts-node 的默认模式会直接报错,而 swc-node 会照常执行,直到运行时才可能崩溃。对于大型重构,这很危险。官方 README 没有回避这一点,它明确说 without typecheck。所以采用它的项目必须保证类型检查在别的环节兜底,比如 IDE 的 TypeScript 服务、pre-commit 钩子、或者 CI 里的 tsc --noEmit。如果团队里有人依赖 ts-node 运行时的类型报错来发现错误,换到 swc-node 会直接失去这层保护。
与 esbuild 的对比:并行是它的差异化优势
README 的基准里,esbuild 是主要对手。单线程转换 esbuild 更快,但并行模式下 @swc-node/core 反超。这说明两者的线程模型不同。esbuild 本身是 Go 写的,单进程内已经很快,但 swc-node 通过 libuv 线程池并行处理多个文件,在 CPU 多核场景下能榨出更多吞吐。不过这需要环境变量 UV_THREADPOOL_SIZE 配合调优,默认线程池大小可能不够。如果你的项目是单文件频繁转译,比如 jest 跑单个测试文件,esbuild 可能更合适。但如果是批量转换,比如整个测试套件或编译大量源文件,swc-node 的并行能力值得一试。
维护状态与升级成本,值得注意的信号
仓库的最近一次发布是 2026 年 7 月的 @swc-node/core@1.15.0,说明核心包还在活跃更新。但 @swc-node/register 的最新版本停在 2024 年 7 月,1.10.9,之后一年多没有新发布。这可能是功能稳定,也可能是维护重心转移。README 里还提到赞助链接,作者在寻求全职开源支持,这是开源项目的常见现实。升级成本方面,由于依赖 SWC 的底层 API,SWC 每次大版本更新都可能影响 swc-node 的兼容性。License 是 MIT,可以自由使用和修改,但如果深入定制,你需要自己跟进 SWC 的变更。
编辑结论
swc-node 适合那些已经接受 transpile-only 工作流、并且被 ts-node 速度困扰的 TypeScript 项目,尤其是大型代码库的本地开发与 jest 测试。它不适合需要类型检查作为拦截关卡的项目,因为 register 和 jest 集成都不做类型诊断。采用前先确认你的 CI 或编辑器已经独立承担类型检查,否则类型错误会悄悄溜进运行时。同时要验证 SWC 的转换语义与你的 tsconfig 目标是否一致,特别是装饰器和枚举这类 SWC 有特殊处理的语法。如果项目已经重度使用 ts-node 的复杂配置,迁移成本需要单独评估。
社区笔记