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 提交进主分支。
结果对齐了,再谈提速;对不齐,一键回滚就是删掉那三行配置——比迁移主编译器安全得多。
评论区
登录后可评论。