编程 把 typescript 换成 typescript-native-bridge:tsc、vue-tsc、tsserver 原样跑在 Go 版 tsgo 上

2026-09-30 00:03:35

把 typescript 换成 typescript-native-bridge:tsc、vue-tsc、tsserver 原样跑在 Go 版 tsgo 上

以 npm 包 typescript-native-bridge(TNB)发布。它是一个 drop-in 的 typescript 替代品:把 typescript 这个包换成这个 fork,tsc、vue-tsc、svelte-check、astro-check、glint、ESLint 和编辑器都照旧用,只是 checker 从 JavaScript 变成在进程内跑的 tsgo(微软用 Go 重写的 TypeScript 编译器)。没有新 CLI、没有新 LSP、不用给每个工具单独配、不改业务代码。

  • 仓库:https://github.com/johnsoncodehk/typescript-native-bridge (Apache-2.0,Microsoft TypeScript 与 microsoft/typescript-go 的衍生作品)
  • npm:https://www.npmjs.com/package/typescript-native-bridge

为什么不能直接用 TypeScript 7(tsgo)

typescript@7 是微软的 Go 原生重写,但它塞不进你实际在用的那些工具:

  • vue-tsc / astro-check / svelte-check / glint 建在经典 typescript 编程接口上(createProgram、Volar hooks、自定义 host)。v7 的编程接口是新的 tsgo API,不是经典接口的 drop-in 替代,这些工具没法直接迁过去。
  • ESLint(typescript-eslint)import 的是经典 typescript API,调用 getTypeChecker() —— 同样是 API 不匹配。
  • 编辑器跑的是 tsserver + Language Service Plugins(.vue 靠 @vue/typescript-plugin),tsgo 的 LSP 不支持这套插件模型。

TNB 保留经典包接口,把 v7 引擎(tsgo 7.x)以进程内方式放在它后面——所以一次 typescript override 就能同时加速上面所有工具。

安装

pnpm(monorepo):

# pnpm-workspace.yaml
overrides:
typescript: npm:typescript-native-bridge@
pnpm install
pnpm exec vue-tsc -b --noEmit

如果包是通过 catalog: 依赖 typescript 的,catalog 条目也要一起改。

npm:

{
"devDependencies": { "typescript": "npm:typescript-native-bridge@" },
"overrides": { "typescript": "$typescript" }
}

按上面这样写 alias 和 $typescript override 引用——把 npm:typescript-native-bridge@… 直接写进 overrides 里,某些 npm 版本会拒绝或者解析错(issue #8)。版本必须精确(例如 6.0.3-bridge.6.tsgo.7.0.2 —— caret range 匹配不上 prerelease),或者用 latest dist-tag。

yarn:resolutions 里写 typescript: npm:typescript-native-bridge@。

本地路径:overrides 里写 typescript: link:../typescript-native-bridge(必须先构建,需要 Go,跑 npm run setup)。

任何 override 改动之后都要重装。override 是全仓库生效的。

确认生效

进程内第一次类型检查时,TNB 会往 stderr 打一行暗色文本:▎ TNB ACTIVE — typescript is the tsgo-backed fork。没有这行 banner,说明加载的还是原版 typescript。

node -e "console.log(require.resolve('typescript'))"

已验证兼容的工具

工具状态验证环境
tsc✅compiler test corpus
vue-tsc✅elk.zone monorepo(约 2,000 文件):报错与 stock 一致,约快 3×
astro-check✅fixture 项目:输出与 stock 相同
svelte-check✅fixture 项目:输出与 stock 相同
glint✅fixture 项目:错误集与 stock 相同
mdx-tsc✅诊断输出与 stock 相同
ESLint + typescript-eslint(type-aware)✅1,000 文件语料:lint 输出与 stock 逐字节一致
tsserver + @vue/typescript-plugin✅volar language-tools 测试套件:205/209 通过

CI gate 会回放 LS probe 语料(约 19k 单元)。

框架相关:.vue / .svelte / .astro / .mdx / .gts 走标准 extraFileExtensions 契约。host 注入的 virtual content 能到达 Go checker。当 host 提供了额外扩展名时,allowArbitraryExtensions 推断为 true。不支持的情况:自定义 resolveModuleNames / resolveModuleNameLiterals 把某个 import 重映射到另一个物理文件(bridge 是同步 JS→Go,tsgo 无法回调 JS resolver)。

性能(Apple Silicon,请以自己的仓库为准)

负载StockTNB
vue-tsc -b 全量检查(elk.zone,约 2,000 文件)9.7s3.2s(约 3×)
type-aware ESLint 单次运行(1,000 文件)2.3s2.4s(+1.5%)
同上,3,000 文件6.9s6.8s(约持平)
JS heap 峰值(1,000 文件 ESLint)769MB631MB(−18%)
整进程 RSS 峰值(vue-tsc -b)1.8GB3.3GB(约 1.9×,结构性的)

规律:时间花在 checker 上的地方,TNB 就更快。vue-tsc 的 whole-program 语义 pass 从约 5.5s 降到约 1.5s;TNB 的 thin program 惰性物化文件。例外是单次 type-aware ESLint:时间花在 parsing / AST 转换 / 规则执行上,每 1,000 文件约 44K 次 checker RPC 都压在 JS↔Go 边界上——这是持平,不是收益。内存收益出现在长时间会话的编辑器路径上。编辑器路径用 V8-arena transport,每个请求约 1.0 次 bridge RPC:quickinfo p50 0.16ms,completionInfo 0.23ms,references 1.5ms。

编辑器 / tsserver(VS Code、Cursor)

CLI 会自动用上 TNB,编辑器不会。

  1. Workspace settings:
{
"js/ts.tsdk.path": "node_modules/typescript/lib",
"js/ts.tsdk.promptToUseWorkspaceVersion": true
}
  1. 命令面板 → TypeScript: Select TypeScript Version → Use Workspace Version
  2. 验证:版本选择器里显示的是 node_modules/typescript/lib 下的路径;Output → TypeScript 通道可能打出 TNB ACTIVE。Vue/Nuxt 用户继续在 tsconfig 的 compilerOptions.plugins 里保留 @vue/typescript-plugin。

行为,以及与 tsgo 的差异

checker 的行为是 tsgo 7.0.2 的,不是原版 TypeScript 6.0.3 的——从 stock 迁移过去,就意味着继承 tsgo 的诊断、内置 lib 和显示输出。TNB 自己对 tsgo 行为的改动是有清单、并由 CI 强制的;例如 getTypeFromTypeNodeWorker 解析类型位置的实体名(issue #30);extra-file-extensions 的处理让 .vue 文件产出 Button.vue.d.ts,而不是 tsgo 的 Button.vue.d.vue.ts(issue #63);whole-program 诊断的确定性(issue #42/#51)。任何看起来与 stock 6.0.3 有差异、又不在清单里的行为,都是 tsgo 本身的行为。

平台支持

bridge 二进制以按平台划分的 optional deps 分发:@typescript-native-bridge/darwin-arm64、darwin-x64、linux-x64、linux-arm64、linux-arm、win32-x64、win32-arm64。Linux 目标为 glibc 2.35。

Alpine/musl 不支持:Go 的 -buildmode=c-shared 运行时在 musl libc 上加载即崩溃(golang/go#13492,一个开了十年、正在修的问题,修复见 golang/go#75048);tsgo 自己的 CLI 能在 Alpine 上跑,只是因为它发的是不带 CGO 的静态二进制。变通做法:在 node:24 或 node:24-bookworm-slim 里跑 typecheck/lint,再部署进 Alpine stage;apk add gcompat 不起作用。

在不支持的平台上会报 unsupported platform or missing optional dependency——那就从源码构建(带 submodule clone,跑 npm run setup,需要 Go + C 工具链)。

排障与回滚

  • 没有 banner:检查 workspace 根目录的 override;pnpm 11 要把 package.json → pnpm.overrides 挪到 pnpm-workspace.yaml 的 overrides;catalog 和 overrides 都要更新;重装。
  • CLI 正常、编辑器不正常:补上 tsdk 设置。
  • 调试慢运行:TSGO_PROFILE=1,退出时会打印 [tsgo-profile] 的 RPC/耗时汇总。
  • 卸载:删掉 override,重装,require.resolve('typescript') 回到 stock 6.x。不需要改任何源码。

推荐文章

程序员茄子在线接单