将 React 代码库升级到 TypeScript 6.0:哪些功能会首先出问题?
TypeScript 6.0更严格的默认值、方法推断修复、#/子路径导入以及Temporal类型对React应用的影响,还有安全的升级检查清单。
来源: TypeScript 6.0 for React Developers: What Actually Changed
大多数编译器升级都难以被察觉。但 TypeScript 6.0 不同:它是基于原始 JavaScript 编译器开发的最后一个版本,其主要作用是为基于 Go 语言的 TypeScript 7 做准备,因此它引入的新特性较少,反而带来了更多暴露现有问题的变更。本指南介绍了 React 项目在升级后的实际体验,并提供了升级检查清单。关于 6.0 在向原生编译器过渡中的定位,可参阅TypeScript 6 在通往原生 TS 7 编译器之路上的桥梁作用。
更严格的默认设置优先应用
TypeScript 6.0 默认启用严格模式,假定使用 ES 模块,并将默认目标版本设置为 es2025。那些明确设置了所有选项的项目不会受到影响;而依赖旧版宽松默认设置的项目则会出现大量新错误。在 React 代码中,通常受影响的都是较旧的组件:类型定义不明确的属性、带有隐式 any 参数的处理函数、可能为 null 的引用,以及没有类型参数的 useState()。这些并非功能退化,而是长期以来存在的歧义,现在终于被明确指出来了。
es3和es5目标版本也已过时,因此最低要求变为es2015。对于许多React应用来说,这一变化影响不大,因为Babel、SWC或esbuild会负责语法降级处理,而tsc仅负责类型检查,但在重新考虑浏览器支持范围之前请务必确认这一点。应在单独的分支上运行升级操作,并进行完整的构建和测试;在周五下午更改编译器默认设置是导致周末泡汤的常见原因。
方法式函数的一致推断
另一个较为隐蔽的问题在于,当某个函数被判定为“上下文敏感”时,意味着在继续推理之前其参数类型必须来自上下文。在 6.0 版本中,只有实际使用了 this 的 function 表达式或方法简写才会被如此处理。此前,即便没有使用 this,选择方法语法而非箭头函数仍可能改变泛型推理结果。下面的示例混合使用了这两种风格:
// Before: inconsistent inference depending on syntax
const useCounter = (initial) => {
const [count, setCount] = useState(initial);
// this pattern used to infer differently than an arrow fn
const increment = function () {
setCount((c) => c + 1);
};
return { count, increment };
};
以前,像 increment 这样的回调函数与等价的箭头函数在推理结果上可能存在差异,而在较大的钩子函数中,这种差异可能会导致较弱的类型传递给调用方。现在,无论为回调函数或返回的辅助函数选择何种语法,都不会再改变最终的类型,从而消除了“为何这个回调未被检查?”这类令人困惑的错误。
使用#/前缀实现更简洁的导入
Node的子路径导入会在package.json的imports字段中定义私有别名,TypeScript 6.0支持在此处使用#/前缀,这与打包工具常用的根级别名格式一致。只需声明一次映射关系即可:
{
"imports": {
"#/*": "./src/*"
}
}
这样组件就可以从项目根目录导入,而无需计算../这样的路径段:
// No more relative path spaghetti in deeply nested components
import { useAuth } from "#/hooks/useAuth";
import { Button } from "#/components/ui/Button";
由于该别名存在于package.json中,而不仅仅是在tsconfig.json的paths配置中,因此Node和TypeScript能够以相同方式解析它,无需额外的插件。此功能仅在会读取package.json中导入信息的解析模式下生效,比如nodenext或bundler模式。
临时类型已内置支持
TypeScript 6.0 包含了 Temporal API 的声明,该 API 已进入 TC39 标准化的最后阶段。那些正在开发调度、预订或仪表板界面的团队无需额外安装包,即可直接使用 Temporal.PlainDate 或 Temporal.ZonedDateTime,而无需再处理 Date 类型。不过这些类型定义并不提供运行时支持:请检查目标浏览器和 Node 版本是否具备原生支持,必要时再添加相应的补丁。
现在就应该升级吗?
是的,应该有计划地升级。虽然 TypeScript 团队将 6.0 宣称为稳定版本,但其内部的变更确实存在:
- 检查
tsconfig.json,锁定所有你依赖的选项。 - 修复旧组件中出现的严格模式错误,而非用
any来掩盖它们。 - 确认哪些工具能够进行向下兼容处理,以及所需的目标版本是什么。
noImplicitOverride,这样被覆盖的成员就必须使用 override 关键字。总结
TypeScript 6.0 并不会改变你编写 React 的方式,但它暴露了那些因宽松默认设置而隐藏的问题。现在按照自己的节奏逐步解决这些问题,远比等到今年晚些时候在从 6 升级到 7 的过程中再处理要便宜得多,而且这样处理的问题主要还是与性能相关。