TypeScript 7.0 RC 深度实战:14 年来最大变革——当编译器从 JavaScript 跑到了 Go,从串行进化到并行,10 倍性能跃迁背后的架构真相
引言:一个编译器的重生
2026 年 6 月 18 日,微软 TypeScript 团队正式发布 TypeScript 7.0 RC。这不是一次常规的版本迭代——这是 TypeScript 自 2012 年诞生以来最彻底的底层重构:整个编译器核心从 TypeScript(编译到 JavaScript 运行)被逐行移植到了 Go 语言。
结果?10 倍性能提升,内存减半。
VS Code 代码库(150 万行)的类型检查从 77.8 秒降至 7.5 秒。TypeORM 从 17.5 秒降至 1.3 秒。这不是什么微优化,这是编译器范式的根本性转变。
本文将从架构设计、并行策略、迁移路径、工程实践四个维度,深度拆解 TypeScript 7.0 的技术内核,并给出真实项目中的落地指南。
一、为什么是 Go?——语言选型背后的工程逻辑
1.1 从自举到原生:TypeScript 编译器的进化史
TypeScript 编译器经历了三个阶段:
阶段一(2012-2020):TypeScript 编译器用 TypeScript 编写,编译为 JavaScript 在 Node.js 上运行。这是经典的「自举」策略——用自己的语言写自己的编译器。优势是生态零摩擦,劣势是 V8 的 JIT 无法突破 JavaScript 单线程和动态类型的固有开销。
阶段二(2020-2025):随着代码库膨胀到百万行级别,编译性能成为开发体验的首要瓶颈。TypeScript 团队尝试了多种优化:增量编译(
--incremental)、项目引用(Project References)、隔离声明(--isolatedDeclarations),但这些都是在 JavaScript 单线程模型下的修补。阶段三(2025-2026):TypeScript 7.0 将编译器核心移植到 Go,实现了原生执行 + 共享内存并行的双重加速。
1.2 为什么不是 Rust?
这是社区最常问的问题。TypeScript 团队给出的理由非常务实:
渐进式移植的可行性:Go 的垃圾回收和简洁的并发模型使得逐行移植 TypeScript 逻辑更为直接。Rust 的所有权系统虽然内存安全更强,但对于编译器这种大量共享不可变数据结构的场景,生命周期标注会成为移植的巨大障碍。
共享内存并行:Go 的 goroutine + 共享内存模型天然适合编译器的并行化——多个 checker worker 需要读取同一份类型声明,共享内存比消息传递高效得多。Rust 更倾向于通过 channel 通信,虽然更安全但对这种读多写少的场景不太匹配。
开发效率:TypeScript 团队需要在一年内完成数百万行代码的移植。Go 的学习曲线更平缓,代码可读性更高,编译速度更快(Go 编译 Go 自身只需秒级)。
二进制分发:Go 的交叉编译能力让 TypeScript 7.0 能够轻松提供 macOS/Linux/Windows 的原生二进制,无需用户安装额外运行时。
1.3 50% 原生 + 50% 并行 = 10 倍加速
官方给出的性能归因非常清晰:
- 约 50% 的加速来自 Go 原生代码的执行效率(无需 V8 JIT 预热、无 JavaScript 动态类型开销、无 GC 压力)
- 约 50% 的加速来自共享内存并行(多核并行解析、并行类型检查、并行 emit)
这意味着,即使你禁用并行(--singleThreaded),TypeScript 7.0 仍然比 6.0 快约 5 倍。而并行化又在此基础上再翻一倍。
二、架构深度解析:从单线程到多层并行
2.1 旧架构:JavaScript 单线程的极限
TypeScript 6.x 的编译管线是严格串行的:
Source Files → Scanner → Parser → Binder → Checker → Emitter → Output
每一步都必须等上一步完成。Checker 是最耗时的环节,通常占编译时间的 60-80%,但它必须严格按依赖顺序逐文件处理——因为类型信息存在全局作用域中,后面的文件依赖前面文件推导出的类型。
V8 的 JIT 可以让热点代码接近原生速度,但:
- 启动时有解析和编译开销
- 单线程无法利用多核 CPU
- JavaScript 的动态特性阻碍了 AOT 优化
- Node.js 的 GC 在处理大型 AST 时频繁触发
2.2 新架构:Go 的多层并行管线
TypeScript 7.0 重新设计了编译管线,每一层都可以并行:
Source Files ──→ [Parallel Parser] ──→ AST Pool
│
┌──────────────────────┤
▼ ▼
[Checker Worker 1] [Checker Worker 2] ... [Checker Worker N]
│ │ │
└──────────┬───────────┘ │
▼ │
[Merge & Resolve] ◄─────────────────────────┘
│
┌──────────┼──────────┐
▼ ▼ ▼
[Emitter 1] [Emitter 2] [Emitter 3]
│ │ │
▼ ▼ ▼
Output Output Output
关键设计点:
2.2.1 并行解析(Parallel Parsing)
解析阶段几乎完美的可并行——每个文件的语法分析完全独立,不存在跨文件依赖。Go 的 goroutine 让文件解析可以自动负载均衡到所有 CPU 核心:
// 简化的并行解析逻辑
func parseFiles(files []string) []*ast.SourceFile {
results := make([]*ast.SourceFile, len(files))
var wg sync.WaitGroup
for i, file := range files {
wg.Add(1)
go func(idx int, path string) {
defer wg.Done()
source := readFile(path)
results[idx] = parser.ParseSourceFile(path, source)
}(i, file)
}
wg.Wait()
return results
}
实测中,百万行代码库的解析时间从秒级降至百毫秒级。
2.2.2 检查器并行化——最难的部分
类型检查是整个编译器最复杂的部分,不能简单地按文件分片并行。原因有三:
全局类型依赖:大多数文件依赖
lib.d.ts中的全局类型(Array、Promise、Record等),独立检查会导致大量重复工作。顺序敏感性:类型推导的结果可能依赖声明顺序。例如两个重载签名的匹配顺序会影响推导结果。
一致性要求:同样的输入必须产生同样的输出,不管并行度如何。
TypeScript 7.0 的解决方案是 固定数量的 Checker Worker + 确定性分片:
// 核心思路:每个 worker 有独立的类型缓存,但分片策略是确定性的
type CheckerPool struct {
workers []*CheckerWorker
fileShards [][]string // 确定性的文件分片
}
func (p *CheckerPool) Check(program *Program) []*Diagnostic {
// 根据文件路径的哈希值确定性地分配文件到 worker
// 相同的输入永远产生相同的分片
shards := p.shardFiles(program.Files)
var allDiags []*Diagnostic
diagCh := make(chan []*Diagnostic, len(p.workers))
for i, worker := range p.workers {
go func(w *CheckerWorker, files []string) {
diags := w.Check(files)
diagCh <- diags
}(worker, shards[i])
}
for i := 0; i < len(p.workers); i++ {
allDiags = append(allDiags, <-diagCh...)
}
return allDiags
}
默认 4 个 checker worker(--checkers 4),可以通过 --checkers 参数调整。更多 worker = 更快但更多内存(因为每个 worker 都有独立的类型缓存副本)。
2.2.3 项目引用并行构建
对于 monorepo,TypeScript 7.0 新增了 --builders 参数,控制同时构建多少个项目引用:
# 同时构建 4 个项目,每个项目用 4 个 checker
tsc --builders 4 --checkers 4
# 注意:最多可能同时运行 16 个 checker,注意内存!
这在 Nx/Turborepo 管理的大型 monorepo 中效果尤为显著——原本需要串行编译 50+ 个包,现在可以并行处理独立的子图。
2.3 共享内存 vs 消息传递
TypeScript 选择的并行模型是 共享内存,这在编译器场景下是正确的选择:
| 维度 | 共享内存(Go) | 消息传递(Rust/Erlang) |
|---|---|---|
| 读取全局类型 | 零拷贝,直接读 | 需要拷贝或序列化 |
| 内存开销 | 一份全局数据 + N 份 worker 缓存 | N 份完整数据副本 |
| 通信延迟 | 纳秒级 | 微秒级 |
| 数据竞争风险 | 需要显式同步 | 编译期保证安全 |
对于读多写少的编译器工作负载(类型声明读一次,用一万次),共享内存的效率碾压消息传递。这是 TypeScript 选择 Go 而非 Rust 的关键技术原因之一。
三、迁移实战:让你的项目从 6.x 无痛升 7.0
3.1 一键安装
npm install -D typescript@rc
npx tsc --version
# Version 7.0.1-rc
没错,还是同一个 typescript 包,还是同一个 tsc 命令。用户体验零变化,速度立刻起飞。
3.2 VS Code 体验升级
安装 TypeScript Native Preview 扩展,即可在编辑器中体验原生 LSP 的速度:
- 自动导入建议:从 500ms+ 降至 <50ms
- 悬停类型提示:即时响应
- 代码重构操作:不再卡顿
- 错误诊断:保存即出
该扩展基于 LSP(Language Server Protocol),理论上 Neovim、Emacs、Zed 等任何支持 LSP 的编辑器都能接入。
3.3 并行调优实战
不同的项目规模和硬件环境需要不同的并行配置。
小型项目(< 5 万行)
默认配置已经足够好,不需要调优:
tsc # 默认 --checkers 4 --builders 1
中型项目(5-50 万行)
适当增加 checker 数量:
# 8 核 CPU,16GB 内存
tsc --checkers 6
大型 monorepo(50 万行+)
需要平衡 checker 和 builder 的并行度:
# 16 核 CPU,32GB 内存
tsc --builders 4 --checkers 4
# 注意:理论上最多 16 个并行 checker,但实际瓶颈在依赖图
# CI 环境(2 核 4GB)
tsc --checkers 2 --builders 1
单线程调试模式
当你需要对比 6.x 和 7.0 的性能差异时:
tsc --singleThreaded
3.4 6.0 与 7.0 并存方案
由于 7.0 暂无稳定的编程 API(要等到 7.1),很多工具(如 typescript-eslint、ts-morph、@component-compiler-webpack-plugin)仍需要 6.x 的 API。微软设计了优雅的共存方案:
{
"devDependencies": {
"typescript": "npm:@typescript/typescript6@^6.0.0",
"typescript-7": "npm:typescript@rc"
}
}
这样:
npx tsc6→ TypeScript 6.x(给 eslint 等工具用)npx tsc→ TypeScript 7.0(给编译用)
@typescript/typescript6 包同时重新导出了 6.x 的编程 API,确保依赖 import * as ts from "typescript" 的工具正常工作。
四、6.0 → 7.0 的破坏性变更清单
TypeScript 7.0 严格遵循 6.0 的语义,但 6.0 中标记为 deprecated 的特性在 7.0 中变成了硬错误。如果你还在 5.x,需要先升 6.0 再升 7.0。
4.1 tsconfig.json 默认值变更
| 配置项 | 旧默认值 | 7.0 默认值 | 影响 |
|---|---|---|---|
strict | false | true | 所有严格检查默认开启 |
module | commonjs | esnext | 输出 ESM 模块 |
target | es5→es2016 | 当前稳定版-1 | 如 2026 年默认 es2025 |
types | ["*"] | [] | 不再自动引入 @types/* |
rootDir | 自动推断 | ./ | 需显式指定 src |
noUncheckedSideEffectImports | false | true | 副作用导入可能报错 |
libReplacement | true | false | lib 替换行为变更 |
stableTypeOrdering | 可关闭 | 强制开启 | 交叉/联合类型顺序稳定化 |
其中最容易踩坑的是 types 和 rootDir:
// 如果你之前依赖了 @types/node 的全局声明
{
"compilerOptions": {
// 必须显式列出,否则 process、Buffer 等全局变量会报错
"types": ["node", "jest"]
}
}
// 如果你的 tsconfig.json 在项目根目录,源码在 src/
{
"compilerOptions": {
"rootDir": "./src" // 7.0 需要显式指定
},
"include": ["./src"]
}
4.2 被移除的特性
以下特性在 6.0 中标记 deprecated,7.0 中直接报错:
target: es5→ 请用es2015或更高downlevelIteration→ 不再需要,ES2015+ 原生支持迭代器moduleResolution: node/node10→ 请用nodenext或bundlermodule: amd//umd/systemjs/none→ 请用esnext或preservebaseUrl→paths现在相对于项目根目录,无需baseUrlmoduleResolution: classic→ 请用bundler或nodenextesModuleInterop: false→ 不能关闭alwaysStrict: false→ 不能关闭- namespace 中的
module关键字 → 请用declare module - import 上的
assert关键字 → 请用with(对齐 ECMAScript import attributes)
4.3 迁移脚本
写一个自动化脚本帮你一次性修复 tsconfig.json:
#!/bin/bash
# migrate_tsconfig.sh - TypeScript 6.x → 7.0 迁移脚本
CONFIG="tsconfig.json"
# 1. 添加 rootDir(如果源码在 src 目录)
if [ -d "src" ] && ! jq -e '.compilerOptions.rootDir' "$CONFIG" > /dev/null 2>&1; then
echo "Adding rootDir: ./src"
jq '.compilerOptions.rootDir = "./src"' "$CONFIG" > tmp.json && mv tmp.json "$CONFIG"
fi
# 2. 显式列出 types
if ! jq -e '.compilerOptions.types' "$CONFIG" > /dev/null 2>&1; then
echo "Adding types: [\"node\"]"
jq '.compilerOptions.types = ["node"]' "$CONFIG" > tmp.json && mv tmp.json "$CONFIG"
fi
# 3. 修复 moduleResolution
RES=$(jq -r '.compilerOptions.moduleResolution // empty' "$CONFIG")
if [[ "$RES" == "node" || "$RES" == "node10" ]]; then
echo "Changing moduleResolution: node → bundler"
jq '.compilerOptions.moduleResolution = "bundler"' "$CONFIG" > tmp.json && mv tmp.json "$CONFIG"
fi
# 4. 移除已废弃的选项
for opt in downlevelIteration baseUrl alwaysStrict esModuleInterop; do
if jq -e ".compilerOptions.$opt" "$CONFIG" > /dev/null 2>&1; then
echo "Removing deprecated option: $opt"
jq "del(.compilerOptions.$opt)" "$CONFIG" > tmp.json && mv tmp.json "$CONFIG"
fi
done
echo "Migration complete. Review changes and test with: npx tsc@rc --noEmit"
五、模板字面量类型的 Unicode 进化
TypeScript 7.0 中一个容易被忽略但影响深远的变更:模板字面量类型现在按 Unicode 码点(Code Point)而非 UTF-16 码元进行推断。
5.1 问题背景
JavaScript 的字符串索引基于 UTF-16 码元。大多数字符(ASCII、CJK)占用 1 个码元,但 emoji 和其他辅助平面字符占用 2 个码元(代理对):
"😀".length // 2 (两个 UTF-16 码元)
"😀"[0] // "\ud83d"(高代理)
"😀"[1] // "\ude00"(低代理)
TypeScript 6.x 的模板字面量类型遵循了这个行为:
type HeadTail<S> = S extends `${infer Head}${infer Tail}` ? [Head, Tail] : never;
type Result = HeadTail<"😀abc">;
// TypeScript 6.x: ["\ud83d", "\ude00abc"]
// TypeScript 7.0: ["😀", "abc"]
5.2 新行为的价值
7.0 的行为更符合人类直觉——一个 emoji 就是一个字符,不应该被拆成两半。这对国际化处理尤其重要:
// 7.0 中可以精确匹配 emoji 前缀
type EmojiPrefix<S> = S extends `${infer E}${infer Rest}`
? E extends Emoji ? [E, Rest] : never
: never;
type Emoji = "😀" | "🎉" | "🚀" | "💡";
type R1 = EmojiPrefix<"😀hello">; // ["😀", "hello"] ✅
type R2 = EmojiPrefix<"🎉世界">; // ["🎉", "世界"] ✅
type R3 = EmojiPrefix<"abc">; // never ✅
在 6.x 中,上面的代码需要处理代理对,非常痛苦。7.0 直接按码点切分,干净利落。
5.3 潜在的兼容性风险
如果你的代码中有基于 UTF-16 码元的模板字面量类型推导,升级 7.0 后可能得到不同结果。需要检查的模式:
// ⚠️ 这类代码在 7.0 中行为会改变
type FirstChar<S extends string> = S extends `${infer C}${string}` ? C : never;
type A = FirstChar<"😀test">;
// 6.x: "\ud83d" (可能依赖此行为做代理对处理)
// 7.0: "😀" (直接得到完整 emoji)
六、watch 模式重写:从轮询到事件驱动
TypeScript 7.0 的 --watch 模式完全重写,基于从 Parcel 的 @parcel/watcher 移植到 Go 的文件监视器。
6.1 旧方案的问题
TypeScript 6.x 的 watch 模式在不同平台表现不一致:
- macOS:使用 FSEvents API,性能尚可
- Linux:使用 inotify,有文件描述符数量限制
- Windows:使用 ReadDirectoryChangesW,延迟较高
- 大型项目的
node_modules监视开销巨大
6.2 新方案的技术细节
TypeScript 团队将 Parcel 的 C++ 文件监视器移植到了 Go:
原版 @parcel/watcher (C++)
│
▼ 移植
Go 版本 (Go + 少量汇编 shim)
│
▼ 集成
TypeScript 7.0 --watch
移植过程中使用了少量平台特定的汇编 shim 来替代 C++ 的系统调用,避免了引入 C++ 工具链依赖。
关键优化:
- 在 macOS 上使用 FSEvents(内核级事件,零轮询)
- 在 Linux 上使用 inotify + 动态 watch 管理
- 在 Windows 上使用 ReadDirectoryChangesW + IOCP
- 智能忽略
node_modules中的不相关变更 - 去抖动(debounce)策略优化,减少频繁重新检查
6.3 实际效果
在大型项目中,watch 模式的 CPU 使用率显著下降,文件变更后的重新检查延迟从秒级降至毫秒级。
# 启动 watch 模式
tsc --watch
# 配合并行参数
tsc --watch --checkers 8
七、CI/CD 集成实战
7.1 GitHub Actions
name: Type Check
on: [push, pull_request]
jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
# 安装 TypeScript 7.0 RC
- run: npm install -D typescript@rc
# CI 环境资源有限,减少并行度
- run: npx tsc --checkers 2 --noEmit
# 可选:对比 6.x 和 7.0 是否产出一致
- run: |
npm install -D @typescript/typescript6@^6.0.0
npx tsc6 --noEmit 2>&1 > ts6_output.txt
npx tsc --singleThreaded --noEmit 2>&1 > ts7_output.txt
diff ts6_output.txt ts7_output.txt && echo "✅ 类型检查结果一致" || echo "⚠️ 存在差异,需排查"
7.2 Docker 构建
FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci && npm install -D typescript@rc
COPY . .
# CI 环境用低并行度
RUN npx tsc --checkers 2 --noEmit
CMD ["node", "dist/index.js"]
7.3 性能基准测试脚本
#!/bin/bash
# benchmark_ts7.sh - TypeScript 6.x vs 7.0 性能对比
echo "=== TypeScript 7.0 RC 性能基准测试 ==="
# 6.x 基线
npm install -D @typescript/typescript6@^6.0.0
echo "Testing TypeScript 6.x..."
START=$(date +%s%N)
npx tsc6 --noEmit
END=$(date +%s%N)
TS6_MS=$(( (END - START) / 1000000 ))
echo "TypeScript 6.x: ${TS6_MS}ms"
# 7.0 单线程
echo "Testing TypeScript 7.0 (single-threaded)..."
START=$(date +%s%N)
npx tsc --singleThreaded --noEmit
END=$(date +%s%N)
TS7_ST_MS=$(( (END - START) / 1000000 ))
echo "TypeScript 7.0 single-threaded: ${TS7_ST_MS}ms"
# 7.0 并行
echo "Testing TypeScript 7.0 (parallel)..."
START=$(date +%s%N)
npx tsc --checkers 4 --noEmit
END=$(date +%s%N)
TS7_P_MS=$(( (END - START) / 1000000 ))
echo "TypeScript 7.0 parallel: ${TS7_P_MS}ms"
echo ""
echo "=== 结果 ==="
echo "原生加速: $(echo "scale=1; $TS6_MS / $TS7_ST_MS" | bc)x"
echo "总加速: $(echo "scale=1; $TS6_MS / $TS7_P_MS" | bc)x"
echo "并行加速: $(echo "scale=1; $TS7_ST_MS / $TS7_P_MS" | bc)x"
八、生态系统兼容性现状
8.1 主流工具兼容情况
| 工具 | 兼容状态 | 说明 |
|---|---|---|
typescript-eslint | ✅ 兼容(需 6.x API) | 使用 @typescript/typescript6 兼容包 |
ts-node | ⚠️ 需要更新 | 7.0 暂无编程 API,ts-node 8.x 将适配 |
ts-morph | ⚠️ 需要更新 | 依赖 TS 编程 API,等 7.1 |
webpack / ts-loader | ✅ 兼容 | 仅调用 tsc CLI,不依赖 API |
esbuild | ✅ 无关 | esbuild 自带类型剥离,不依赖 tsc |
Vite | ✅ 兼容 | 通过 @vue/language-tools / @tsx-js/parser 独立解析 |
Rollup + @rollup/plugin-typescript | ✅ 兼容 | 仅调用 CLI |
Next.js | ✅ 兼容 | 使用 fork-ts-checker-webpack-plugin |
Nuxt | ✅ 兼容 | 类型检查独立进程 |
tsc --noEmit | ✅ 完美兼容 | 推荐的 CI 检查方式 |
8.2 编程 API 空缺的影响
TypeScript 7.0 暂不提供稳定的编程 API(ts.createProgram() 等),这意味着:
- AST 变换工具(ts-morph、ts-patch、ts-transform)暂时无法直接使用 7.0
- 自定义 Emit 插件需要等 7.1
- 社区迁移方案:在 CI 中并行运行 7.0 做类型检查 + 6.x 做代码生成
{
"scripts": {
"typecheck": "tsc --noEmit",
"build": "tsc6 --outDir dist",
"check-and-build": "npm run typecheck && npm run build"
}
}
九、深层思考:编译器移植的工程哲学
9.1 逐行移植 vs 重写
TypeScript 团队选择了「逐行移植」而非「从零重写」,这是一个深思熟虑的决策:
- 语义一致性:十四年积累的测试套件(数万个测试用例)可以直接验证移植后的行为一致性
- 增量验证:可以逐模块对比两个编译器的输出,快速定位移植引入的 bug
- 降低风险:避免「第二系统综合症」,不会在重写中不知不觉改变语义
这种方式看似笨拙,实则是大工程迁移最可靠的方法。Linux 内核从 x86 移植到 RISC-V 采用了类似策略——先翻译,再优化。
9.2 性能与正确性的平衡
--checkers 参数可能导致不同并行度产出不同结果(在极端情况下),这是一个有意的设计权衡:
- 编译结果的确定性是理想,但强串行会牺牲并行收益
- 实践中,顺序依赖的错误极其罕见
- 团队的建议:在 CI 中固定
--checkers值以消除不确定性
9.3 对行业的影响
TypeScript 7.0 的成功证明了:
- 编译器不需要永远用自己写的语言——用户关心的是速度和正确性,不是编译器的自举
- Go 在编译器工程中的适用性——继 Docker、Terraform、Hugo 之后,TypeScript 可能是 Go 在编译器领域最大的胜利
- 渐进式迁移的可行性——一年内完成百万行代码的移植,证明 Go 作为目标语言在大型代码迁移中的优势
十、总结与展望
TypeScript 7.0 RC 带来的不仅是 10 倍性能提升,更是一次编译器架构的范式转换:
| 维度 | TypeScript 6.x | TypeScript 7.0 |
|---|---|---|
| 实现语言 | TypeScript → JavaScript | Go(原生二进制) |
| 执行模型 | 单线程(V8 JIT) | 多线程(goroutine 共享内存) |
| 启动时间 | 百毫秒级(Node.js 启动) | 毫秒级(原生二进制) |
| 内存开销 | 较高(V8 堆 + GC) | 减半(Go 内存池 + 并行分片) |
| 分发方式 | npm(需 Node.js) | npm(内含原生二进制) |
| 编程 API | 完整 | 暂缺(7.1 补齐) |
| 类型语义 | - | 与 6.0 严格一致 |
升级建议:
- 立即行动:所有项目都可以安全地用 7.0 做类型检查(
tsc --noEmit) - 并行过渡:通过
@typescript/typescript6兼容包让工具平稳迁移 - 关注 7.1:编程 API 的回归将解锁 ts-morph、typescript-eslint 等工具的原生支持
TypeScript 用十四年证明了一件事:类型系统是 JavaScript 生态的基础设施。 而 7.0 证明了另一件事:基础设施也需要与时俱进,哪怕是推倒重来。
当编译器从解释型语言跑到编译型语言,从串行跑到并行,从前端跑到了系统编程——这不仅仅是 TypeScript 的重生,更是整个前端工程化走向成熟的标志。