TypeScript 6.0 Beta在2月11号发了,这个版本很特殊——它是最后一个用JavaScript写的TypeScript编译器版本。下一个大版本TypeScript 7.0会用Go重写,利用原生代码和多线程并行来提速。6.0本质上是从JS时代到Go时代的过渡桥梁,很多改动都是为了让你提前适配7.0。我第一时间装上跑了一圈,记录下实际体验和踩坑。
安装和第一个惊喜
npm install -D typescript@beta
确认版本:
npx tsc --version# Version 6.0.0-beta
装完随手建个项目跑一下,第一个坑就来了。写了个简单的函数:
function bar(x) { return x; }
直接报错:
error TS7006: Parameter 'x' implicitly has an 'any' type.
原因是strict现在默认为true了。以前不写strict配置默认是false,现在反过来了。如果你的老项目没显式写"strict": true,升级后会多出一堆类型错误。解决方案:在tsconfig.json里显式加"strict": false。但说实话,都2026年了,不开strict的项目确实该反思一下了。
默认值大改:几乎每个老项目都会踩
6.0改了一堆默认值,这是最容易出问题的地方。我整理了一张表:
| 选项 | 旧默认值 | 新默认值 | 影响 |
|---|---|---|---|
| strict | false | true | 没显式配的项目会多很多类型错误 |
| module | commonjs | esnext | CJS项目需要显式指定 |
| target | es3 | es2025 | 不再默认降级编译 |
| rootDir | 自动推断 | tsconfig.json所在目录 | 输出目录结构可能变化 |
| types | [“*”](扫描全部) | [](空) | 需要显式声明@types包 |
| noUncheckedSideEffectImports | false | true | 副作用import拼写错误会报错 |
其中rootDir的变化最坑。实测如下:假设项目结构是:
project/ tsconfig.json src/ index.ts
tsconfig.json里只写了"include": ["./src"],没有显式设rootDir。以前TS会自动推断rootDir为./src,输出到dist/index.js。现在6.0默认rootDir是tsconfig.json所在目录,输出变成了dist/src/index.js,而且会报一个错误:
error TS5011: The common source directory of 'tsconfig.json' is './src'. The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.Visit https://aka.ms/ts6 for migration information.
修复很简单,显式写上rootDir:
{ "compilerOptions": { "rootDir": "./src", "outDir": "./dist" }, "include": ["./src"]}
types默认改为空数组也很关键。以前不写types,TS会扫描node_modules/@types下所有包自动引入。现在默认不引入任何东西,你需要显式声明:
{ "compilerOptions": { "types": ["node"] }}
不然连console.log都用不了(因为console类型在@types/node里)。想恢复旧行为可以写"types": ["*"],但官方不推荐——显式声明能提升构建性能。
新特性:Temporal API类型支持
等了好几年的Temporal API,TypeScript终于内置类型了。配置"lib": ["esnext"]或"target": "esnext"即可使用:
let yesterday = Temporal.Now.instant().subtract({ hours: 24 });let tomorrow = Temporal.Now.instant().add({ hours: 24 });console.log(`Yesterday: ${yesterday}`);console.log(`Tomorrow: ${tomorrow}`);
编译通过,类型推导完整。Temporal比Date好用太多了——不可变、时区感知、API清晰。不过要注意:类型支持归类型支持,运行时还得看你的Node.js版本或浏览器是否已经实现了Temporal。目前Deno和部分新版浏览器已经支持。
新特性:Map.getOrInsert
ECMAScript的upsert提案到stage 4了,TypeScript 6.0直接加了类型支持:
const cache = new Map<string, number[]>();// 以前的写法if (!cache.has("key")) { cache.set("key", []);}const items = cache.get("key")!;items.push(42);// 现在一行搞定const items2 = cache.getOrInsert("key", []);items2.push(42);
还有个getOrInsertComputed,适合默认值计算开销大的场景:
someMap.getOrInsertComputed("key", () => { return computeExpensiveValue();});
这个API简单实用,少写了一堆样板代码。
新特性:RegExp.escape
终于不用自己写正则转义函数了:
const userInput = "hello (world) [test]";const escaped = RegExp.escape(userInput);// escaped: "hello \\(world\\) \\[test\\]"const regex = new RegExp(`\\b${escaped}\\b`, "g");
配合es2025 target使用。
方法语法推断修复
这个修复虽然小但很实用。以前用method syntax写对象字面量里的函数,属性顺序会影响类型推断:
declare function callIt<T>(obj: { produce: (x: number) => T, consume: (y: T) => void,}): void;// 箭头函数 - 没问题callIt({ consume: y => y.toFixed(), produce: (x: number) => x * 2,});// 方法语法 - 以前会报错!y推断为unknowncallIt({ consume(y) { return y.toFixed(); }, produce(x: number) { return x * 2; },});
6.0修复了这个问题。原因是方法语法有隐式的this参数,TS以前把它当成”上下文敏感”的函数,推断优先级低。现在如果函数里没用到this,就不算上下文敏感了。
为TypeScript 7.0做准备:–stableTypeOrdering
这个flag是专门为6.0到7.0迁移准备的。背景是TypeScript 7.0用Go重写后会做并行类型检查,但并行意味着类型的内部ID分配顺序不确定了。为了保证输出一致性,7.0改用了基于内容的确定性排序算法。这会导致union类型的显示顺序和6.0不同。举个例子:
export function foo(condition: boolean) { return condition ? 100 : 500;}// 6.0可能输出: 100 | 500// 7.0会输出: 100 | 500(确定性排序)
看起来一样?加个看似无关的变量就不一样了:
const x = 500;export function foo(condition: boolean) { return condition ? 100 : 500;}// 6.0输出: 500 | 100(因为500的typeID先分配)// 7.0输出: 100 | 500(确定性,不受声明顺序影响)
开启--stableTypeOrdering可以让6.0的排序行为对齐7.0。但官方说了这flag会让类型检查慢25%左右,所以只在迁移对比时用。
Subpath Imports支持 #/ 前缀
Node.js的subpath imports以前不支持#/开头,只能写#root/xxx这种。现在Node.js支持了,TypeScript 6.0跟进了:
{ "name": "my-package", "type": "module", "imports": { "#": "./dist/index.js", "#/*": "./dist/*" }}
import * as utils from "#/utils.js";
终于可以用#/代替@/了,而且这是Node.js原生支持的,不需要bundler。在moduleResolution设置为node20、nodenext或bundler时可用。
dom.iterable不用单独引入了
以前要遍历DOM集合,tsconfig的lib要同时写dom和dom.iterable:
// 以前需要 "lib": ["dom", "dom.iterable"]for (const el of document.querySelectorAll("div")) { console.log(el.textContent);}
现在dom.iterable和dom.asynciterable已经合进dom了,写"lib": ["dom"]就够了。不大的改动,但少了一个经常忘记的配置项。
升级建议
如果你打算从5.x升级到6.0,需要注意的事情不少。官方出了个ts5to6迁移工具,可以自动调整baseUrl和rootDir。我建议的升级步骤:
- 先装beta跑一下
tsc --noEmit,看有多少错误 - 处理默认值变化:加上显式的rootDir、types、module配置
- 如果错误太多,先加
"ignoreDeprecations": "6.0"临时过渡,但7.0不会支持这个选项 - 开
--stableTypeOrdering跑一次,提前发现7.0会遇到的类型排序差异 - 逐步修复类型错误,大部分是加显式类型注解就能解决
该不该现在升级
Beta阶段不建议生产项目直接上。但强烈建议现在就拉个分支试一下,因为6.0的breaking changes不少,越早知道自己的项目受多大影响越好。这个版本真正让我期待的其实是TypeScript 7.0。Go重写+并行类型检查,大型项目的编译速度应该会有质的飞跃。6.0就是在给那个未来铺路。