TypeScript 7 是快了,但 vue-tsc、ESLint、编辑器一个都用不上——今天一个 override 把 Go 引擎塞回了旧 API 底下

TypeScript 7 把编译器搬进 Go 之后,最尴尬的不是「快不快」,而是「你根本用不上」。

VS Code 代码库的类型检查从 125.7 秒压到 10.6 秒,官方测的 8~12 倍,这些数字都真的。可只要你项目里有 .vue、.svelte、.astro 文件,或者 CI 里跑着 typescript-eslint 的类型感知规则,这套提速就跟你没关系——因为那些工具卡在的不是速度,是 API。

为什么换不掉:三种工具都焊在旧 API 上

tsgo(也就是 @typescript/native-preview 里那个二进制)是新的程序化 API,跟经典 typescript 包的程序化表面不是一回事:

  • vue-tsc / astro-check / svelte-check / glint:都是基于经典 API 写的——createProgram、Volar 的 extraFileExtensions、给 .vue 注入虚拟 getSourceFile、包装 createProgram。tsgo 的独立 CLI / LSP 不认这套协议,你把 CLI 换成 tsgo,vue-tsc 照样慢。
  • typescript-eslint:@typescript-eslint/parser 直接 import typescript,调 createProgram / getTypeChecker()。瓶颈在 JS 那侧的 checker,不在 ESLint 的 AST 遍历上。你换 CLI 没用,因为 ESLint 根本不走 CLI。
  • 编辑器:VS Code 里 Volar(@vue/typescript-plugin)是作为一个 tsserver 的 Language Service Plugin 跑的。微软的 tsgo LSP 预览版不支持这个插件模型——迁移过去意味着 .vue 集成直接丢。

一句话:不是这些工具不想快,是它们连接口都对不上。

TNB 的思路:保留旧 API,把 Go 引擎塞进去

typescript-native-bridge(简称 TNB)干的事情很不「编译」——它不让你换 CLI,也不让你换 LSP,而是保留 typescript 这个包名和它的经典 API 表面,在进程内把后端换成 tsgo。

你原来的调用链:

你的代码 → createProgram() → (JS checker)  → 类型检查结果

TNB 替换后:

你的代码 → createProgram() → createTsgoProgram() → (Go checker) → 类型检查结果

对调用方来说,函数名、签名、返回结构都没变;只是 createProgram 内部把任务路由到了 Go。Volar 给 .vue 造的虚拟内容,通过进程内 overlay 喂给 Go——不走 IPC,所以也没有独立 tsgo LSP 那种跨进程开销。

结果就是:一个 override,三条工具线一起加速,而且工具本身零改动。

怎么接:三行配置,一条 override

pnpm(monorepo,放 pnpm-workspace.yaml 根部):

overrides:
  typescript: npm:typescript-native-bridge@

npm(package.json):

{
  "overrides": {
    "typescript": "npm:typescript-native-bridge@"
  }
}

yarn(package.json):

{
  "resolutions": {
    "typescript": "npm:typescript-native-bridge@"
  }
}

改完必须重装依赖(pnpm install / npm install)。这个 override 是仓库级的——vue-tsc、@typescript-eslint/parser,以及所有间接引用 typescript 的东西都会自动捡到这份 fork。

有个坑要先记住:版本号得精确钉死,比如 6.0.3-bridge.16.tsgo.7.0.2,caret 范围匹不上预发布版本。另外 npm 某些版本不接受把 npm:typescript-native-bridge@… 直接塞进 overrides,要走别名 + $typescript 引用(官方 issue #8):

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

怎么确认它真的生效

进程内第一次跑类型检查时,TNB 会往 stderr 打一行横幅:

┌─────────────────────────────────────────────────────────┐
│ ✅ TNB ACTIVE — typescript is the tsgo-backed fork      │
└─────────────────────────────────────────────────────────┘

没看到横幅 = 加载的还是原版 typescript。快速自查:

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

输出应该指向 typescript-native-bridge,而不是 node_modules/typescript@6.x。

数字:不是「理论上更快」

TNB 的兼容性不是嘴上说的,它给了一张「已验证工具」表,每个都有对照基准:

  • vue-tsc:elk.zone monorepo(约 2,000 文件),输出错误与原生一致,约 3 倍快
  • astro-check:fixture 项目输出与原版完全相同
  • svelte-check:输出相同(含 svelteHTML ambient shims)
  • glint:错误集合与原版一致(含 .gts 转换后的虚拟文件)
  • mdx-tsc:诊断输出一致(走 Volar 的 runTsc,错误映射回 MDX 源位置)
  • typescript-eslint(类型感知规则):1,000 文件语料上,lint 输出与原版逐字节一致
  • tsserver + @vue/typescript-plugin:volar language-tools 测试套件 205/209 通过(4 个 skip)

还有一个持续验证的 CI 门:每次 push/PR 和每晚,都会拿语言服务的探测语料(quickinfo / definition / references / diagnostics,约 19,000 个单元)跟同一个原版构建对跑,不允许出现新的分歧。

这个「逐字节一致 + 语料对跑」的设计,是它敢叫 drop-in 的底气:不是「我快一点」,而是「我先证明结果没变,再谈快」。

别忽略的边界

  • 状态是 Experimental:官方明说不保证稳定性和正确性,TypeScript 一发新版就可能挂,得跟着版本管理。
  • 只覆盖标准 Compiler API 能驱动到的工具:自定义 resolveModuleNames / resolveModuleNameLiterals(把 import 重映射到另一个物理文件的那种)不支持——因为 JS→Go 的桥是同步的。碰到这类先跑一遍你的真实构建。
  • 平台限制:需要 Node.js + C/C++ 构建工具链(native addon)。
  • 大 monorepo 未充分验证:官方没给固定加速承诺,让你在自己仓库上测。

下一步(今天就能做)

如果你是 Vue/Svelte/Astro 项目,又馋 TS7 的速度——别急着把主编译器换成 typescript@7,那条路会丢掉 vue-tsc / Volar / typescript-eslint。更稳的一步是:

先只改一个 override 到 TNB,跑一次你平时的 typecheck 脚本(pnpm exec vue-tsc -b --noEmit),确认 stderr 出现 TNB ACTIVE 横幅、且错误数跟改之前一模一样,再决定要不要把这条 override 提交进主分支。

结果对齐了,再谈提速;对不齐,一键回滚就是删掉那三行配置——比迁移主编译器安全得多。

评论区

0 条评论

登录后可评论。