这节在地图里是"回望"。我们不从年份流水账讲起,而是抓一条主线:TypeScript 始终把"不破坏 JavaScript 语义"当底线,所有类型能力都叠在 JS 之上、编译后消失。理解这条线,你就不会把它当成另一种语言去学。
TypeScript 起因是微软内部维护大型 JS 应用的痛苦——没有类型,重构像拆盲盒。它从第一天就定调:是 JS 的超集,不是替代品。这意味着任何合法 JS 都是合法 TS,迁移零改写成本。
// 纯 JS 直接就是合法 TS function add(a, b) { return a + b; } // 在其上渐进加类型,而不重写 function add(a: number, b: number): number { return a + b; }
输入输出:第一段无需改动即可被 tsc 接受;第二段是同一逻辑加契约。这条"渐进"路径是它能普及的根本——对比那些要求重写的强类型语言,TS 对存量项目友好得多。
几个有代表性的能力节点(不列全,挑影响写法的):
strict 族与 strictNullChecks:把"空值"从隐式可接受变成显式处理,是契约严格度的大跃迁。这张图把"能力随版本叠加、但编译后归零"的双层结构画出:

把几个影响写法的版本挑出来,能看清"能力是逐步叠加、而非推倒重来":
enum 的 const 形式与模块语法雏形,兼顾 JS 模块化的过渡期。null/undefined 纳入类型系统、strictNullChecks 可开启,空值从"隐式可接受"变"显式处理",是本册第二章非空判断的源头。keyof 与映射类型,类型编程(第五章)第一次有了系统性工具。project references)与元组/剩余元素改进,支撑第九章大型工程的增量检查。const 断言(as const)落地,让字面量收窄与只读推断更顺手(第三章有讲)。label 声明、unknown 全面铺开,配合 strict 持续收紧。extends 推断占位增强,第五章高级玩法的主要燃料。const 类型参数、多配置文件支持,进一步贴近 ECMAScript 方向。这张图把"能力随版本叠加、但编译后归零"的双层结构画出:

一个真实场景:老代码依赖宽松推断,升到 4.1 后模板字面量类型变精细,部分拼接字符串的类型被收紧。
// 升级前(3.x 宽松,推断为 string) type EventName = "click" | "hover"; function makeHandler(name: EventName) { return name; } const h = makeHandler("click" + "" as string); // 旧版可能被放过 // 升级后(4.1 更严格,模板字面量推断到具体字面量) type EventName = "click" | "hover"; function makeHandler<N extends EventName>(name: N): N { return name; } // 传入 string 会报错:类型 "string" 不满足约束 "EventName" // 修法:明确标注字面量,或修正源头让拼接落在允许集合内 const ok = makeHandler("click"); // 通过
操作:先把 typescript 锁进 devDependencies 目标版本,CI 用同一版本;跑 tsc --noEmit 看增量错误清单;把报错按"源头类型设计问题"与"真正需收窄"两类分批处理。结果:多数报错是上游标注含糊,补上显式类型后一次过。解读:升级成本几乎都来自"以前靠宽松推断蒙混",正是契约松懈的债。
TS 极少破坏运行时语义,但类型层面的破坏性变更仍会发生,典型两类:
// 1) lib.d.ts 随 target 变化:旧代码用 ES5 target 时 Promise 类型缺失 // 升级 target 到 ES2017+ 后,Promise/async 才有完整类型,无需 polyfill 声明 // 反模式:target 过低又手写过时声明,冲突 // 改写:target 对齐所用语法,删掉多余声明文件 // 2) 函数重载解析变严:更精确的候选优先 function pick(a: string): string; function pick(a: number): number; function pick(a: any) { return a; } const v = pick("x"); // 4.x 稳定命中 string 重载,推断更准 // 旧版若把宽泛签名放前,会吞掉精确分支,见第九章重载陷阱
背景:breaking change 多半在 strict 相关开关与重载解析。操作:升级前读版本说明的"行为变更"段。结果:配合 CI 全量检查,影响面可控。解读:TS 的兼容纪律比多数语言好,但"零成本升级"的前提是你本来就写得严格。
TS 团队的原则是类型层面可以超前探索,但运行时语义严格对齐 ECMAScript。所以 enum、namespace 这类"非标准"构造一直被边缘化,而 class、可选链、?? 等则紧跟标准。
// TS 很早支持,但等标准落地后才成"标准写法" const v = obj?.maybe ?? "默认"; // 可选链+空值合并,已是 ES2020
背景:有人担心"TS 引入非标准特性会分裂 JS"。操作:观察官方对 enum 的冷淡、对标准语法的追捧。结果:你的 TS 代码大多能直接当现代 JS 看。解读:这降低了"被锁定"的风险。
团队项目锁 minor 版本进 devDependencies,CI 用同一版本,避免"我机器 5.3 你 4.9"的解释差异(第一章讲过)。新大版本通常向后兼容,但 strict 相关改进可能让旧代码冒新错——升级后跑全量 tsc --noEmit 看增量错误,分批修。
⚠️ 别把 enum/namespace 当成 TS 主推写法。它们偏离 ECMAScript 标准,官方态度冷淡,新项目优先用字面量联合与 ES 模块,避免被非标准构造绑定。
💡 判断一个 TS 特性该不该用,问"它编译后是不是标准 JS"——是就放心用(如可选链、泛型),不是就谨慎(如 enum),这条线能避开大多数锁定风险。