跳到正文

我所理解的 TypeScript 最佳实践(6):TypeScript 7 升级与工具链兼容

我所理解的 TypeScript 最佳实践

  1. 为什么选择 TypeScript
  2. 严格模式
  3. 状态、映射与泛型
  4. 模块边界与 TypeScript 脚本
  5. 类型检查、Lint 与测试
  6. TypeScript 7 升级与工具链兼容(当前)

升级 TypeScript 时,先分清调用的是 tsc 命令,还是编译器的 JavaScript API。命令行检查、ESLint 读取类型信息、Vue 单文件组件检查,各自经过不同入口。这个区别决定了能否直接替换 typescript 依赖。

TypeScript 7.1 Beta 和当前工具版本

截至 2026 年 11 月 3 日,npm 的 typescript@latest 是 7.0.2,typescript@beta 是 7.1.0-beta。7.1 Beta 于 10 月 20 日发布,加入 es2026 的目标和标准库选项,也带来了新的编译器 API。TypeScript 7.1 Beta 公告

7.1 把 API 分在多个入口里。typescript/sync 和 typescript/async 提供编译器客户端,typescript/ast 提供语法树和枚举。旧工具直接调用的 require("typescript").createProgram 在 7.1 Beta 中仍是 undefined,而 typescript/sync 的 API.prototype.createProgram 已可用。要支持 7.1,工具需要接入新的调用方式。

7.1 Beta 在应用代码里带来的变化

  • 导入属性可以进入环境模块声明。 一个 .d.ts 可以按 with { type: "text" } 给导入结果声明类型,省掉为每种文本扩展名单独写声明。模块能否加载仍由运行时或打包器决定。
  • 支持源码阶段导入。 import source 和 import.source() 可以表达先取模块源码对象、再决定如何实例化的流程,例如先拿到 WebAssembly 模块,再提供宿主导入。应用仍需确认目标环境支持该语法。
  • 新增 es2026 的 target 和 lib 选项。 标准库声明包含 Error.isError()、Map.prototype.getOrInsert() 和 Uint8Array 的 Base64 相关方法。设置 lib 不会替运行环境补实现。Promise.allKeyed 和 Iterator.zip 等提案方法则进入 esnext。
  • 分布式条件类型的约束检查更严格。 如果 B extends A,而条件类型正在把联合类型 A 逐个成员拆开,B 可能只满足整个联合的约束,不能当成某个分支成员的子类型。升级后这类代码可能需要在分支里补充约束。DOM 声明也有更新,迁移时应看实际诊断。

7.0 的主要变化是 Go 原生编译器和语言服务。7.1 Beta 在此基础上开放新 API,继续优化内存和检查性能,并带来上面的语法和标准库变化。TypeScript 7.0 公告 · TypeScript 7.1 Beta 公告

ESLint 与 Vue 类型检查的兼容情况

工具 当日版本与兼容情况
TypeScript 稳定版 7.0.2,7.1.0-beta 可单独试用。
@typescript/typescript6 npm latest 为 6.0.2,提供 TypeScript 6 兼容入口与 tsc6。兼容包版本和实际加载到的编译器版本要分别查看。
typescript-eslint parser 和插件为 8.72.1,声明支持 >=4.8.4 <6.1.0。TS7 原生支持仍在上游跟踪。 兼容范围 · TS7 支持进度
ESLint 10.14.0。核心版本和 parser 的 TypeScript 支持范围分别判断。 发布说明
vue-tsc 3.3.14。当前 CLI 仍通过 typescript/lib/tsc 启动,并能识别 TS6 兼容包的 alias。 发布说明

在 Windows、Node 26.12.0 的隔离安装里,tsc --version 输出 7.1.0-beta。把同一个 Beta 包直接装在 typescript 名下后,parser 8.72.1 加载失败,报错文案仍写着 TS 7.0。vue-tsc 3.3.14 --version 则因 ./lib/tsc 未导出而退出。对这组工具,稳定版可以继续用并行安装,Beta 可以通过独立入口试跑普通 TypeScript 项目的检查。

下文的版本和耗时来自 2026 年 8 月对 TypeScript 7.0.2 的实测。

为什么值得试

TypeScript 7.0 官方发布文章把它定义成一次 Go native port。官方给出的几个大型仓库全量构建耗时分别是:VS Code 从 125.7 秒到 10.6 秒,Sentry 从 139.8 秒到 15.7 秒,Playwright 从 12.8 秒到 1.47 秒;几个样本里的内存占用也同步下降。

这个变化对 Agent 辅助开发也很关键。Agent 每次改完代码以后,最常做的是跑类型检查、读错误、继续修。tsc 从十几秒降到几秒,单次看着只是少等一会儿,连续迭代时体感会完全不一样。

VS Code 团队迁移时,先在低风险区域运行 TypeScript 7,再逐步扩大覆盖面。主仓库类型检查从 36 秒降到 5 秒,npm run watch 从大约 80 秒降到 20 秒出头。这些结果支持尝试 TS7,而分阶段迁移的做法也提醒我们,大项目需要逐步确认各部分工具链是否兼容。

The Register 对这次发布的报道也提到了本地全量类型检查的成本变化。本文只比较执行时间,不据此推断设备温度、耗电或风扇状态;这些指标需要另外测量。

直接替换会撞到 typescript-eslint

第一步我先按最直接的方式替换:

pnpm add -D typescript@^7.0.2

安装本身能完成,pnpm exec tsc --version 也会变成:

Version 7.0.2

项目里的两个 tsconfig 也能通过:

pnpm run typecheck:build-scripts
pnpm run typecheck:code-lab

问题出在周边工具链。pnpm peers check 会报 @typescript-eslint/parser、@typescript-eslint/typescript-estree、@typescript-eslint/project-service 和 @typescript-eslint/tsconfig-utils 需要的 peer 范围仍然是 typescript >=4.8.4 <6.1.0。

更关键的是运行时也会直接失败。只要 import @typescript-eslint/parser,它就会抛出这段错误:

typescript-eslint does not support TS 7.0.
Please see https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/#running-side-by-side-with-typescript-6.0 to run typescript-eslint using the TS 6 API.
See also https://github.com/typescript-eslint/typescript-eslint/issues/10940 for tracking typescript-eslint's support for TS >=7.1

这不是一个可以靠忽略 peer warning 混过去的问题。TypeScript 7.0.2 的包根入口只提供版本信息。直接 require('typescript') 只能拿到版本信息:

{
  version: '7.0.2',
  keys: ['version', 'versionMajorMinor'],
  hasScriptTarget: false,
  hasCreateProgram: false
}

typescript-eslint 这类工具需要 createProgram、ScriptTarget、AST 和类型关系 API。TypeScript 7 的 tsc 能跑,不代表所有把 TypeScript 当库调用的工具都能跑。typescript-eslint 的跟踪 issue也把这个方向标成还需要外部 API 支持。

这次采用并行安装

官方发布文章专门给了 side-by-side 方案:TypeScript 7 提供新的 tsc,同时用 @typescript/typescript6 给仍然需要旧 API 的工具链提供兼容包。

这个仓库最后采用的是:

{
  "devDependencies": {
    "@typescript/native": "npm:typescript@^7.0.2",
    "typescript": "npm:@typescript/typescript6@^6.0.2",
  },
}

这两个名字的职责要分清楚:

  • pnpm exec tsc 解析到 TypeScript 7.0.2,用来跑项目类型检查。
  • pnpm exec tsc6 可以显式跑旧编译器,当前输出是 6.0.3。
  • require('typescript') 仍然拿到 6.0.3,并且有 createProgram、ScriptTarget 等旧 API。
  • @typescript-eslint/parser 继续从 typescript 这个包名读取 TS6 API,所以不会被 TS7 的 API 缺口打挂。

并行安装后再跑同一个 parser 验证,结果恢复正常:

{
  astType: 'Program',
  servicesKeys: [
    'emitDecoratorMetadata',
    'experimentalDecorators',
    'isolatedDeclarations',
    'program',
    'esTreeNodeToTSNodeMap',
    'tsNodeToESTreeNodeMap'
  ]
}

并行安装后,项目的 CLI type-check 使用 TS7,工具链里调用 TypeScript JS API 的部分继续使用 TS6。

后来我在一个 ESLint plugin 包里又验证了一次,这类包还要多看一层依赖身份。普通应用通常只在构建、类型检查和 lint 阶段用到 typescript,把 TS6 alias 放在 devDependencies 就够了;但如果发布后的规则代码会继续 import ts from 'typescript',它就不是纯开发依赖。比如规则运行时要读取 enum、literal union、类型标注或 ts.TypeChecker,typescript 这个包名就应该在安装包里继续解析到 TS6 API。

这个 ESLint plugin 包最后写成了下面这样:

{
  "dependencies": {
    "@typescript-eslint/utils": "^8.71.0",
    "typescript": "npm:@typescript/typescript6@^6.0.2",
  },
  "devDependencies": {
    "@typescript/native": "npm:typescript@^7.0.2",
    "@typescript-eslint/parser": "^8.71.0",
  },
}

这个差异只影响依赖分组,不改变并行安装的核心逻辑:pnpm exec tsc 仍然走 TS7,import('typescript') 仍然拿到 TS6 API。验证时看三个入口最直接:

pnpm exec tsc --version
pnpm exec tsc6 --version
node -e "import('typescript').then((ts) => console.log(ts.version, typeof ts.createProgram))"

如果这个包希望把 TypeScript 交给使用方控制,也可以改成 peer dependency,但要同步写清安装要求和支持范围。对现在这个阶段来说,让插件包自带 TS6 API,更适合避免使用方项目里意外解析到 TS7。

同一个项目,CLI 和 TypeScript API 走不同入口

2026 年 8 月在 Windows、Node 25.8.1 与 pnpm 11.17.0 下通过构建和 18 个源码包测试;实际 API 版本另外用 import 探针确认。完整内容见TS 版本与实际验证弹窗。

小仓库里的速度也能看出来

实测仓库的 TypeScript 体量不大,pnpm exec 自己的启动开销也会占掉不少时间,所以不能拿这里的倍数去类比大仓库。不过同一台机器、同一组命令里,TS7 仍然更快。

目标                   TS7 最快    TS6 最快
build-scripts          1246.4ms    2483.1ms
client/code-lab        1023.5ms    1767.6ms

这组数据包含 pnpm exec 包装开销,而且取的是最快样本,不是完整统计。它只能说明这台机器上的这两个项目有提速,不能预测其他仓库的倍数。迁移时,仍应在自己的项目里分别测量冷启动与增量检查的耗时,并核对 ESLint、构建插件等工具的兼容性。

总结

这次升级能成立,是因为命令行编译器和工具使用的 TypeScript API 可以分别选择版本:tsc 走 TS7,typescript 包名继续提供 TS6 API,依赖它的 typescript-eslint 才能正常工作。本文的小仓库样本显示类型检查提速,周边构建、测试和依赖检查也通过了;速度数字包含包装开销且取最快样本,不能直接外推到其他仓库。插件发布后仍会调用 TypeScript API 时,还要把这份依赖放在运行时可用的位置。

据此,我会把 TS7 升级分成三种情况判断:

  • 只跑 tsc --noEmit 的项目:优先试 TypeScript 7。先跑 TS6 和 TS7 对照,再看类型错误是否一致。
  • 有 typescript-eslint、Vue、Svelte、Astro、Angular 模板类型检查的项目:先核对各工具的支持范围。需要 TS6 API 时采用并行安装;CLI 和框架检查各自使用已验证的版本,不把 tsc 升级成功当成整条链路兼容。
  • 依赖 TypeScript programmatic API 的构建插件或 loader:不要只看 tsc 是否通过。必须直接 import 对应插件、跑真实构建或测试,因为 TS7.0 的 API 缺口会在这里暴露。

是否可以直接升级,取决于 parser、框架检查器和插件是否已经适配 TS7 的 API。切换前,用实际项目的 TSConfig 跑一轮 tsc,再分别跑启用类型信息的 lint 和框架检查。

TS 版本与实际验证

本地验证结果

当时的验证环境是 Windows、Node v25.8.1、pnpm 11.17.0。升级后跑了这些命令:

pnpm peers check
pnpm run typecheck:build-scripts
pnpm run typecheck:code-lab
pnpm run build:code-lab
pnpm run build:music-player
pnpm run test:kits
pnpm exec hexo clean
pnpm exec hexo generate
git diff --check

结果都是通过。pnpm run test:kits 覆盖了 18 个源码包,包含 @typescript-eslint/parser、Vitest、Stylelint 规则、Vue script setup 规则和几个文章配套工具包。它比单独跑 tsc 更能证明周边工具链没有被 TS7 切换误伤。

另外我专门跑了 pnpm peers check。直接替换成 TS7 时这里会失败;改成并行安装后,结果变成:

No peer dependency issues found

在 ESLint plugin 包里,额外跑了 pnpm run pack:dry-run,确认发布产物入口、声明文件和 dist 都能在 TS7 编译下生成;pnpm peers check 也通过了,说明 peer 依赖声明匹配。实际加载到的版本和 API,仍用前面的 import 探针检查,不能只凭 peer 检查判断。