TypeScript 6 正式发布:最值得关注的重点、坑点与升级建议

0 阅读10分钟

TypeScript 6 正式发布:最值得关注的重点、坑点与升级建议

TypeScript 6.0 已经在 2026 年 3 月 23 日正式发布。
如果只看一句话总结,那就是:

TS6 不是一个“炫技型”大版本,而是一个为 TypeScript 7 铺路的过渡版本。

它最重要的意义,不是突然多了很多全新语法,而是把很多旧时代配置和兼容包袱清理掉,同时把默认值往“现代 JavaScript / ESM / Bundler / Evergreen Runtime”这条路上统一。

所以这次更新,真正值得关注的不是“新语法有多酷”,而是“默认行为变了哪些、你的项目会不会直接报错”。

这篇文章我尽量只讲最重要的部分,并配上简单示例,方便你快速判断:

  • TS6 值不值得升
  • 升级最容易踩哪些坑
  • 有哪些新能力值得立刻用起来

一、先说结论:TS6 最重要的 3 件事

1. TS6 是 TS7 的桥梁版本

官方已经讲得很明确:TypeScript 6.0 是最后一个基于当前 JavaScript 代码库的正式大版本,后面的 TypeScript 7 会基于新的 Go 原生实现继续演进。

这意味着 TS6 的很多变化,核心目标都不是“单独优化自己”,而是:

  • 尽量和 TS7 行为对齐
  • 提前清理不适合继续保留的老配置
  • 降低未来迁移到 TS7 的成本

如果你后面打算跟进 TS7,那 TS6 基本就是绕不过去的一站。

2. 这次最有体感的变化,是默认值和配置策略

TS6 对很多编译配置的默认行为做了调整,比如:

  • strict 默认变成 true
  • module 默认变成 esnext
  • target 默认跟随当前年份的 ES 版本,目前是 es2025
  • types 默认变成 []
  • rootDir 默认变成当前 tsconfig.json 所在目录

这类变化的特点是:不一定看起来“新”,但非常可能影响老项目。

3. 语法层面的亮点有,但都比较务实

TS6 也有一些很实用的新能力,比如:

  • 更好的方法推断
  • 支持 #/ 子路径导入
  • 新增 Temporal 类型
  • 支持 es2025
  • RegExp.escapeMap.getOrInsert 等新标准库类型

这些都不是“革命性语法”,但胜在能很快落地。

二、最值得关注的几个新变化

1. 对象里的函数,推断终于没那么别扭了

这是我认为最容易被低估的一项改进。

以前 TypeScript 在一种很常见的场景里会有点“看人下菜碟”:

  • 如果你在对象里写的是箭头函数,推断通常没问题
  • 但如果你写的是更自然的对象方法语法,推断有时就会突然变差

比如下面这两种写法,看起来只是长得不一样:

const obj1 = {
  consume: (y) => y.toFixed()
};

const obj2 = {
  consume(y) {
    return y.toFixed();
  }
};

很多人会更喜欢第二种,因为它更像正常写对象。
但在旧版本里,第二种写法在某些泛型场景下,TypeScript 反而更容易“犯迷糊”,把参数推成 unknown

例如:

declare function callIt<T>(obj: {
  produce: (x: number) => T;
  consume: (y: T) => void;
}): void;

callIt({
  consume(y) {
    return y.toFixed();
  },
  produce(x: number) {
    return x * 2;
  },
});

在旧行为里,上面这种写法里的 y 可能会被推成 unknown
TS6 修了这个问题:如果这个对象方法里根本没有用到 this,那 TypeScript 就别再自己吓自己了,该正常推断就正常推断。

这带来的直接好处是:

  • 你可以更放心地写对象方法
  • 泛型推断更稳定
  • 少写一些“本来不该补”的显式类型

如果你平时喜欢写配置对象、工具对象,或者各种对象式 API,这个改动会舒服很多。

2. 终于支持 #/ 子路径导入

Node 的 imports 字段本来就支持子路径导入,但以前你必须写成像 #root/* 这种形式,不能直接从 #/ 开始。

TS6 跟进了 Node 的新能力,现在在 moduleResolution: "nodenext"moduleResolution: "bundler" 下,可以直接这样配:

{
  "name": "my-package",
  "type": "module",
  "imports": {
    "#/*": "./src/*"
  }
}

然后代码里这样写:

import { sum } from "#/utils/sum.js";

这件事的价值在于:
你终于可以少写一堆 ../../.. 了,而且写法比传统相对路径更稳定。

如果你现在本来就在用 bundler、Node ESM 或 Bun,这个特性很值得考虑。

3. types 默认变成 [],性能更好,但也是最容易踩坑的地方之一

这是 TS6 升级时最需要注意的改动之一。

过去,TypeScript 会默认把 node_modules/@types 里很多声明包自动扫进来。
这样虽然“省心”,但代价也很明显:

  • 多项目仓库里会加载大量没必要的类型
  • 构建和编辑器响应都会变慢
  • 全局类型来源不够可控

TS6 现在把默认值改成了:

{
  "compilerOptions": {
    "types": []
  }
}

如果你的项目依赖 Node 全局类型、测试框架全局类型等,就需要显式写出来:

{
  "compilerOptions": {
    "types": ["node", "jest"]
  }
}

如果你升级后突然看到这些报错:

  • Cannot find name 'process'
  • Cannot find name 'describe'
  • Cannot find module 'fs'

那大概率就是这里。

我的建议很简单:

  • Node 项目:先加 "types": ["node"]
  • 有测试框架:再补 jestmochavitest/globals 对应配置
  • 不要偷懒回退成 "types": ["*"],除非你真的只想临时救火

4. rootDir 默认变成当前目录,输出路径可能会变

TS6 以前会推断一个公共源目录作为 rootDir
现在默认更简单了:直接把 tsconfig.json 所在目录当成 rootDir

这会导致一个很典型的问题:

假设你的项目结构是:

project/
  tsconfig.json
  src/
    index.ts

你以前可能输出的是:

dist/index.js

升级后如果没显式配置 rootDir,可能会变成:

dist/src/index.js

解决办法也很直接:

{
  "compilerOptions": {
    "rootDir": "./src",
    "outDir": "./dist"
  },
  "include": ["src"]
}

如果你升级后发现打包产物目录层级变深了,优先看这个配置。

5. TS6 支持 es2025 和更多新标准库类型

TS6 新增了 es2025target / lib 支持。

虽然 ES2025 本身没有那种“看得见摸得着”的全新语法炸点,但它补上了不少新标准库类型,比如:

  • RegExp.escape
  • Promise.try
  • Iterator 方法
  • Set 方法

例如 RegExp.escape 终于不用手写转义函数了:

function matchWholeWord(word: string, text: string) {
  const escaped = RegExp.escape(word);
  const regex = new RegExp(`\\b${escaped}\\b`, "g");
  return text.match(regex);
}

这种 API 的意义不在于“多高级”,而在于它让很多过去靠手写工具函数的逻辑,终于能收敛到标准库。

6. 内置了 Temporal 类型

如果你之前关注过 JavaScript 日期时间 API 的演进,那应该知道 Temporal 这些年一直很受期待。

TS6 现在已经内置了它的类型定义。只要你使用 esnext 相关 lib,就能直接获得类型支持:

const yesterday = Temporal.Now.instant().subtract({
  hours: 24,
});

const tomorrow = Temporal.Now.instant().add({
  hours: 24,
});

这不代表所有运行时已经完全原生可用,但至少在类型层面,TS 已经把它视为现代 JavaScript 的正式方向之一。

三、升级 TS6 时最容易踩的坑

如果你没时间把发布说明从头看到尾,那至少记住下面几个点。

1. strict 现在默认是 true

如果你的项目之前没有显式写过 strict,升级后可能会突然多出一批类型错误。

你有两个选择:

  1. 直接接受新默认值,顺手把类型债一起还掉
  2. 先显式写回 "strict": false,分阶段迁移

如果是老项目,我更推荐先稳住:

{
  "compilerOptions": {
    "strict": false
  }
}

等升级完成后,再逐步往回收紧。

2. baseUrl 不再作为模块查找根目录

这是一个很关键的兼容性变化。

如果你以前这样写:

{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@app/*": ["app/*"]
    }
  }
}

TS6 推荐改成更明确的写法:

{
  "compilerOptions": {
    "paths": {
      "@app/*": ["./src/app/*"]
    }
  }
}

也就是说,别再指望 baseUrl 作为一个隐式前缀去兜底。

3. moduleResolution: "classic" 已经被移除

如果你还在用:

{
  "compilerOptions": {
    "moduleResolution": "classic"
  }
}

那就该换了。
官方给出的现代方向很明确:

  • bundler
  • nodenext

大多数现代前端项目,我会优先考虑 bundler
Node ESM 项目,则优先考虑 nodenext

4. esModuleInterop: false 这条老路也走不通了

TS6 不再允许把下面这些设置成 false

  • esModuleInterop
  • allowSyntheticDefaultImports

也就是说,和 CommonJS 的互操作行为,现在默认就按更安全、也更现代的方式走。

过去你可能这么写:

import * as express from "express";

现在很多场景下更推荐:

import express from "express";

如果你项目里还大量依赖旧导入方式,升级时最好跑一轮检查。

5. outFile 被彻底移除了

如果你还在靠 TypeScript 自己把多个输入文件拼成一个输出文件,那 TS6 会直接告诉你:别这么干了。

现在这件事应该交给 bundler:

  • esbuild
  • Vite
  • Rollup
  • Webpack
  • Parcel

TS 团队的态度很明确:TypeScript 更应该专注在类型检查和声明生成,而不是继续兼任 bundler。

6. 旧的 import assertions 写法要改成 with

以前这样写:

import data from "./data.json" asserts { type: "json" };

现在要改成:

import data from "./data.json" with { type: "json" };

这个改动不复杂,但如果你项目里有 JSON import、WASM import、CSS module attribute 之类的实验性写法,升级前最好全局搜一下。

7. 在有 tsconfig.json 的目录里直接跑 tsc foo.ts,现在会报错

这是一个很容易忽略,但实际非常常见的变化。

以前很多人会在项目目录里顺手执行:

tsc src/index.ts

但如果当前目录里已经有 tsconfig.json,TS6 现在会直接提示你:
这个配置文件不会被加载。

如果你本来就是想忽略配置、只按默认行为编译单个文件,那需要显式写成:

tsc --ignoreConfig src/index.ts

这个改动的目的其实很合理,就是避免“明明项目有配置,但命令行行为和你想的不一样”这种隐性坑。

四、TS6 到底值不值得升?

我的判断是:

值得尽快升级的人

  • 你正在维护现代前端项目
  • 你已经使用 ESM / bundler / NodeNext
  • 你准备后面跟 TS7
  • 你本来就想顺手清理旧 tsconfig

这类项目升级 TS6 的收益是明确的,因为配置方向和生态方向是一致的。

可以先观察一下的人

  • 很老的历史项目
  • 有大量自定义构建链
  • 还依赖 classicoutFilebaseUrl 老行为
  • 有很多隐式全局类型和非严格模式代码

这类项目不是不能升,而是最好把它当成一次小型迁移项目来做,不要只改个版本号就上线。

五、一个更实用的升级顺序

如果你准备从 TS5.x 升到 TS6,我建议按这个顺序来:

  1. 先升级依赖,但保守保留旧配置行为。
  2. 显式补上 strictrootDirtypes 等关键配置,先让结果稳定。
  3. 搜索并替换明显废弃项,比如 assertsmoduleResolution: "classic"outFile
  4. 检查 baseUrlpaths,避免隐式路径查找失效。
  5. 最后再考虑吃掉 TS6 的新默认值,而不是一开始就全盘接受。

一个比较稳的过渡型 tsconfig.json 可以像这样:

{
  "compilerOptions": {
    "target": "es2025",
    "module": "esnext",
    "moduleResolution": "bundler",
    "strict": true,
    "rootDir": "./src",
    "outDir": "./dist",
    "types": ["node"]
  },
  "include": ["src"]
}

如果你只是想先压住过渡期告警,也可以临时加上:

{
  "compilerOptions": {
    "ignoreDeprecations": "6.0"
  }
}

但这只是过渡手段。因为官方已经明确说了:这些废弃项到了 TS7 不会再被支持。

六、最后

看完官方文档之后,我对 TS6 的感受其实很明确:

它不是那种“看完让人立刻兴奋到想重写项目”的版本,
但它很像一次必要的基础设施升级。

如果你只盯着“有没有惊艳语法”,会觉得 TS6 好像没那么炸;
但如果你站在未来一两年的工程演进上看,它其实非常重要。

因为从 TS6 开始,TypeScript 的官方态度已经很清楚了:

  • 拥抱现代 ESM
  • 拥抱 bundler
  • 拥抱更严格的默认值
  • 清理旧时代兼容包袱
  • 为 TS7 的原生化铺平道路

所以我的建议是:

新项目可以直接按 TS6 的思路建,老项目则应该尽早做一次有计划的迁移。

参考资料