TypeScript 6与7:更智能的推断机制,以及后续基于Go语言的重写。
了解TypeScript 6如何弥补键推断的缺陷并优化默认值设置,从而为TypeScript 7基于Go语言彻底重写编译器奠定基础。
最近,TypeScript在两个不同方面不断改进,这两点共同揭示了一个事实:这种语言正变得既更智能又更快。在重大架构变革之前发布的最后一个基于传统JavaScript编译器的版本——TypeScript 6,主要致力于修复长期存在的推理缺陷并优化默认设置。而TypeScript 7则采取了更为激进的措施,用Go语言重写编译器本身,追求的是极致的速度而非新的语法特性。将这两者结合起来看,就能明白该工具链是如何发展到如今状态的——先是提升了准确性,随后又实现了显著的速度提升。
TypeScript 6中的更智能推理功能
凡是曾经输入过方法简写,然后看到参数悄然被替换为 any 的人,都能体会 TypeScript 旧版推断规则带来的困扰。TypeScript 6 解决了这一问题,同时也弥补了多年来一直让开发者苦苦寻找答案的几处其他推断缺陷。
此前,任何在内部引用 this 的函数都被视为“上下文敏感型”,这意味着编译器会完全跳过对其参数类型的推断——即便在函数体中根本从未使用过 this 的情况下也是如此:
// Old behavior: 'user' silently became 'any'
const handlers = {
onSave(user) {
console.log(user.name); // no autocomplete, no error
},
};
在 TypeScript 6 中,只有当函数内部真正使用了 this 时,编译器才会将其视为上下文敏感的函数。如果没有使用 this,则会恢复正常的类型推断机制,参数将获得其预期的类型,而不会退化为 any:
// TypeScript 6: 'user' is correctly inferred from context
const handlers: Handlers = {
onSave(user) {
console.log(user.name); // fully typed
},
};
这看似只是一个小小的技术调整,但对日常开发却有着深远的影响,尤其是在充斥着对象方法和事件处理程序的 React 和 Next.js 代码库中。
TypeScript 6 还为 using 声明提供了第一类支持,从而实现了更规范的资源管理方式。无需再手动将清理逻辑封装在 try/finally 块中,一旦变量超出作用域,编译器就会自动处理其释放工作:
function readConfig() {
using file = openFile("./config.json"); // auto-disposed at scope end
return JSON.parse(file.read());
}
子路径导入也会变得更简洁。现在的 #/ 前缀可以通过 imports 字段实现,这样就能避免使用一长串的相对路径 ../../../:
{
"imports": {
"#/*": "./src/*"
}
}
import { formatCurrency } from "#/utils/currency";
// instead of: import { formatCurrency } from "../../../utils/currency";
还有多个默认值发生了变化。target选项的默认值现已从过时的ES3改为ES2023,module的默认值为ESNext,而moduleResolution的默认值则为。types选项的默认值也变成了空数组,这样TypeScript就不会自动扫描并加载它能找到的所有@types包。据微软称,仅这一项改动就使构建速度提升了20%到50%,因此即便你对采用任何新语言特性不感兴趣,也值得仔细检查一下你的tsconfig.json文件。
总体而言,TypeScript 6 的建议包括:检查代码库中那些方法较多的对象,因为它们或许能免费获得自动的类型安全改进;在处理需要清理的文件、连接或定时器时使用 using 语句;不要默默地沿用新的编译器默认设置——应在 tsconfig.json 中明确指定这些设置,以便清晰表达你的意图;同时预计 React、Redux 以及 Tailwind 工具等框架和库所提供的类型定义也将在未来几周内跟上这些变化。TypeScript 6 并非临时版本——它显著减少了日常开发中可能遇到的类型推断问题,这本身就已足以成为升级的理由。
TypeScript 7 的彻底重写
TypeScript 6改进了编译器对类型的处理方式,而TypeScript 7则着眼于更根本的问题:整个工具链的运行速度。微软用Go语言完全重写了编译器、语言服务以及相关工具,取代了多年来一直支撑TypeScript的本地JavaScript实现。这并非简单的版本升级——它被称作该语言历史上最大的性能改进。
那些曾盯着编辑器中的加载转圈图标等待类型错误出现的用户,一定能体会到这次重写所要解决的痛点。
由于团队是在原有编译器逻辑的基础上进行优化,而非从头构建类型检查规则,因此在整个过渡过程中,现有代码的兼容性基本保持不变。
微软自己公布的测试数据展现了改进的幅度。在大约150万行的VS Code代码库中,原本需要约125秒才能完成的完整构建现在仅需大约10秒。在编辑器中检测到第一个类型错误的时间从约17秒缩短到了1.5秒以内。内存使用量减少了大约18%,而语言服务器的崩溃次数则下降了60%以上。这些数据并非纯粹通过模拟测试得出——Slack、Figma、Google、Notion和Vercel等公司在新编译器发布前就将其应用于实际生产项目,并反馈称其在非控制环境下的测试中依然能保持这些优势。
对于使用 React 和 Next.js 的团队而言,这会带来实实在在的日常好处。在大型单仓库项目中,过去等待 tsc 检测类型不匹配可能需要数秒时间;而在基于 Go 的编译器下,这一耗时大幅缩短:
// Before: waiting on tsc to catch this in a large monorepo could take seconds
interface UserCardProps {
name: string;
avatarUrl?: string;
onSelect: (userId: string) => void;
}
function UserCard({ name, avatarUrl, onSelect }: UserCardProps) {
// With TS7's native checker, this feedback loop is nearly instant
return (
<button onClick={() => onSelect(name)} className="rounded-lg p-2 hover:bg-slate-100">
{avatarUrl && <img src={avatarUrl} alt={name} className="h-8 w-8 rounded-full" />}
<span>{name}</span>
</button>
);
}
拥有数百个组件的大型 Next.js 应用程序不再像以往那样将类型检查视为持续集成过程中的瓶颈。那些使用了大量泛型类型的项目,比如同时结合了 Tailwind 和 Redux 的项目,其增量构建速度明显更快。在大型单仓库项目中,编辑器自动补全的功能响应速度也大幅提升。
在升级之前,有几点需要注意的要点。严格模式现已成为默认设置,因此此前较为宽松的代码库可能会出现新的错误。es5这样的旧版编译目标以及过时的模块解析设置不再只是警告,而是会被视为严重错误。此外,由于本次版本中的编程接口仅部分稳定,任何直接依赖它的框架或工具都应等到 TypeScript 7.1 版本后再进行升级。
这一切都无需新的语法或截然不同的语法结构——TypeScript 7 的出现主要是为了解决长期存在的编译器性能问题。如果你的团队正在维护庞大的 React 或 Next.js 代码库,这一版本终于能让 tsc 不再让人感觉像是在与之抗争。与任何重大基础设施变更一样,最好先在分支上测试;你的持续集成流程会因你的谨慎而受益。
相关阅读
- TypeScript 7 的 Go 语言重写:对 React 类型安全的影响 — 了解 TypeScript 7 基于 Go 语言的编译器如何加快构建速度、优化泛型推断,从而消除 React hooks 和 JSX 中的隐藏
any类型。