TypeScript 5.9、6.0 和 7.0 到底该怎么选

太阳作者太阳
原创内容采用 CC-4.0 协议发布,转载请注明出处
TypeScriptNext.jsBuntypescript-eslint

我在迁移 Bun 项目依赖时,把 TypeScript 统一到了 5.9.3。紧接着就发现 npm 上不只有 6.0.2,还有 6.0.3,而且 latest 已经变成 7.0.2

这组版本号确实容易让人误判。6.0 为什么只有几个补丁版本就直接到 7?Next.js 16 为什么还经常搭配 5.9?既然 Bun 能直接运行 TypeScript,是不是可以不要 TypeScript 包?

先给结论:TypeScript 6 不是占位包,TypeScript 7 也不是简单的下一个小升级。6.0 是旧 JavaScript 编译器的最后一个稳定大版本,7.0 切换到了 Go 原生实现,并暂时拿掉了旧的程序化 API。

先看当前真实版本

截至 2026 年 8 月 29 日,npm 上的状态是:

  • typescript@latest7.0.2
  • TypeScript 6 最新补丁为 6.0.3
  • TypeScript 5 最后一个稳定版本线是 5.9.x

可以直接查询,不必根据搜索结果或记忆猜:

bun pm view typescript version
bun pm view typescript@6 version

返回:

7.0.2
6.0.3

npm 的 TypeScript 版本列表也能看到完整发布记录。版本信息变化很快,文章里的数字只是这个日期的快照。

TypeScript 6 是过渡版本,但不是过渡包

微软在 TypeScript 6.0 发布说明中把 6.0 称为 5.9 与 7.0 之间的 bridge release。这里的“过渡”描述的是升级作用,不是说它只负责转发命令或没有真正编译器。

TypeScript 6.0 仍然是完整的 JavaScript 版 tsc 和语言服务,也是这套旧代码库的最后一个大版本。它加入新默认值、弃用旧配置,为 7.0 的行为做准备。

为什么没有 6.1、6.2?TypeScript 团队早在 7.0 开发期间就说明,6.0 是最后一条 JavaScript 实现版本线,后续只在必要时发布 6.0.16.0.26.0.3 这类补丁。版本号低不代表被放弃,只表示这条线不再增加新的 minor 功能。

TypeScript 7 为什么直接换了底座

TypeScript 7.0 发布说明介绍了新的 Go 原生编译器和语言服务。官方给出的定位是约 10 倍性能提升,并利用共享内存并行处理大型项目。

7.0 延续了 6.0 的类型检查和命令行行为,但默认配置发生了变化,包括:

  • strict 默认开启。
  • module 默认 esnext
  • types 默认空数组。
  • noUncheckedSideEffectImports 默认开启。
  • rootDir 的默认推断发生变化。
  • 6.0 已弃用的选项在 7.0 中直接报错。

更大的兼容边界不是语法,而是 API。TypeScript 7.0 暂时不提供旧 typescript 包的程序化 API,官方计划在 7.1 提供新的 API。

这意味着只调用 tsc 的项目可能很容易升级,直接 import ts from 'typescript' 的工具却不能原样切换。

Next.js 16 没有把项目锁在 5.9

Next.js 16 升级文档给出的 TypeScript 最低版本是 5.1,并没有要求必须使用 5.9.3。

很多 Next.js 项目停在 5.9,有几个现实原因:

  • 创建项目或锁文件时,5.9 是当时的稳定版。
  • 框架支持 TypeScript,不代表 ESLint、代码生成器和语言插件都支持最新编译器。
  • 团队不想把框架升级、运行时迁移和 TypeScript 大版本迁移放进同一个变更。

所以“Next.js 16.3.3 使用 TypeScript 5.9.3”这个说法不准确。应用选择了 5.9.3,不等于 Next.js 内部固定使用它,更不等于 Next.js 不支持 6.0。

当前项目真正受 typescript-eslint 约束

这个博客使用 eslint-config-next,它会带入 typescript-eslint。当前锁文件中的 typescript-eslint 8.68.0 声明 TypeScript peer range:

>=4.8.4 <6.1.0

因此:

  • 5.9.3 在支持范围内。
  • 6.0.3 也在支持范围内。
  • 7.0.2 超出当前 peer range。

这个范围与 TypeScript 7 暂时缺少程序化 API 是一致的。typescript-eslint 不只是执行 tsc,它需要读取 TypeScript AST 和类型信息,不能只换一个原生二进制就结束。

TypeScript 7 官方给出的过渡方案是并行安装:用 TypeScript 7 的 tsc 做命令行检查,同时通过 @typescript/typescript6 或 npm alias 给依赖旧 API 的工具保留 TypeScript 6。

这个方案适合类型检查成本很高、愿意维护双版本的项目。对一个博客来说,它增加的依赖解释成本可能比节省的几秒检查时间更大。

Bun 不会替代 TypeScript 类型检查

Bun 可以直接执行 .ts

bun scripts/task.ts

但这里主要做的是语法剥离和运行,不等于完整项目类型检查。Bun 不会因为成功运行一个脚本,就替你验证所有泛型、声明文件和未执行分支。

项目仍然需要:

bun --bun tsc --noEmit

在 TypeScript 5 和 6 中,tsc 本身还是 JavaScript CLI,--bun 可以确保它由 Bun 执行。TypeScript 7 的 tsc 已经是原生程序,届时“由 Bun 执行 TypeScript 编译器”这个说法也需要改变:Bun 负责 package script,Go 编译出的 tsc 负责类型检查。

为什么这次先统一到 5.9.3

这次分支已经同时改变了:

  • package manager 和锁文件。
  • Next.js 开发与构建执行器。
  • Docker builder 与 runner。
  • 测试运行器。
  • 内容扫描和本地脚本 API。
  • API 路由组织方式。

我选择 5.9.3,不是因为 6.0.3 有缺陷,也不是 Next.js 要求,而是想保留一个已经验证过的 TypeScript 基线,不在同一次迁移里再处理 6.0 的弃用与默认值变化。

这个决定是控制变量,不是长期版本建议。

如果只看当前 peer range,下一步完全可以单独把 Blog 和 content workspace 升到 6.0.3,运行类型检查、ESLint、测试和生产构建,再处理 6.0 的弃用提示。这样失败时很容易判断是 TypeScript 变化,而不是 Bun 迁移留下的问题。

三个版本应该怎样选

继续使用 5.9.3,适合:

  • 正在进行另一项大型迁移。
  • 现有工具链已验证稳定。
  • 暂时不需要 TypeScript 6/7 的新默认值和性能。

升级到 6.0.3,适合:

  • 工具 peer range 已支持 <6.1.0
  • 准备清理 7.0 会删除的配置。
  • 仍然需要完整的旧 TypeScript 程序化 API。

升级到 7.0.2,适合:

  • 主要调用 tsc,不依赖旧编译器 API。
  • 大型项目确实能从原生并行检查获益。
  • ESLint、Vue、Svelte、Astro、MDX 或其他嵌入式工具已经给出明确兼容方案。
  • 团队愿意在必要时并行保留 TypeScript 6。

我不会再根据“主版本号更大”直接升级,也不会因为 6.0 只发布到 .3 就把它当成临时包。先看调用的是 CLI 还是 API,再看整个工具链的 peer range,答案通常就很清楚了。

模块解析本身还有另一组容易混淆的问题,见我为什么把 TypeScript 的 NodeNext 改成 Bundler