这节在地图里是全册最后一站,也是回到现实的决策层。前面九章给了你所有"怎么写",这里给"什么时候该用、什么时候不该、往哪看"。技术选型不是追新,是基于契约收益与成本的权衡。
模板字面量类型、更智能的推断、更精细的变型控制,让"类型即契约"能描述更复杂的业务规则。本册第五章的类型编程会持续增值——你能用类型挡住的 bug 会越来越多。
// 趋势示例:路由与参数在类型层绑定(已在 5.4 展示) type Route = "/users/:id"; // 未来这类"类型即文档、类型即校验"会更常见,减少运行时 schema 负担
但第五章方法论的刹车仍然有效:表达力越强,越要克制。类型不是越复杂越好。
第六章、第九章讲的慢,官方长期在投入——原生编译器移植、增量与项目引用优化,目标让大规模类型检查也进秒级。这对"契约能否持续运转"是关键,因为慢会逼团队放弃严格度。
# 趋势:更快的检查命令会成为默认体验 npx tsc --incremental # 已是常态 # 原生移植后整体检查速度量级提升(关注版本说明)
第七章讲的"前后端共享一份契约"会越来越主流,配合运行时校验(zod)做到"编译期+运行期"双保险。Single source of truth 从口号变成工具链默认能力。
1. 项目生命周期多长?短期脚本不必上 TS,长期维护强上。 2. 团队规模多大?一人可松,多人必须严格 + CI 卡类型。 3. 是否接框架?React/Vue/Node 生态类型成熟,收益高。 4. 能否接受编译步骤?纯静态页或极小脚本,JS 更轻。
这张图把"是否引入 TS"的决策流给出:

把"是否引入 TS、引入到什么严格度"做成一张可直接照抄的表:
| 场景 | 团队规模 | 生命周期 | 建议严格度 | 落地动作 |
|---|---|---|---|---|
| 一次性脚本/构建 glue | 1 人 | 周级 | 关 strict | 直接 JS,或仅加 // @ts-check |
| 内部中台工具 | 2-5 人 | 年级 | 开 strict | CI 卡 tsc --noEmit,baseline 控错误 |
| 对外 SDK/库 | 跨团队 | 多年 | strict + declaration | 输出 .d.ts,skipLibCheck 友好 |
| 大型前端应用 | 5+ 人 | 多年 | strict + noUncheckedIndexedAccess | 项目引用 + 增量检查(第九章) |
| 全栈服务 | 多人 | 多年 | strict + 共享契约 | 前后端共享类型 + zod 运行时双保险 |
表里每一行的"严格度"不是越高越好,而是与"生命周期 × 团队规模"匹配。短期脚本上全套严格反而拖慢交付,长期多人项目松严格度则契约红利消失。
类型生态正朝"编译期 + 运行期一致"收敛,几个判断:
// 趋势:schema 与类型单一来源(zod 为例) import { z } from "zod"; // 伪示意,运行时库 const UserSchema = z.object({ id: z.number(), name: z.string() }); type User = z.infer<typeof UserSchema>; // 类型由 schema 推出,单一来源 const parsed = UserSchema.parse(input); // 运行期校验,编译期也有类型 // 这种"一份定义、两侧受益"会越来越多,第七章共享契约是同一思路
该跟进的:模板字面量类型、标准语法(可选链、??、顶层 await)、运行时校验一体化。该观望的:偏离 ECMAScript 的实验构造、把复杂逻辑硬塞进类型层(第五章刹车仍有效)。判断准绳依旧是那句话——"它编译后是不是标准 JS"。
把第四章工程化落到一个最小可跑的骨架,比讲理念更实在:
// tsconfig.json 关键项(节选) // { // "compilerOptions": { // "strict": true, // "noUncheckedIndexedAccess": true, // "noEmit": true, // 类型检查与产物分离 // "incremental": true, // 配合第九章提速 // "skipLibCheck": true // }, // "include": ["src"] // } // 提交钩子用一条命令卡住退化 // npx tsc --noEmit && 通过才允许提交 // 基线控制:先 allowJs 接纳存量,再逐文件收紧
背景:strict 全开常让老项目一堆红。操作:先开 strict 跑全量,把错误数记进 baseline,新文件不许新增,旧文件按模块消化。结果:团队不被一次性红浪劝退,契约逐步立起。解读:落地 TS 是工程管理问题,不是语法熟练度问题——环境顺了,严格度才推得动。
编译器越来越会自己猜。早几年要显式标注的返回类型、泛型参数,现在多数能由上下文推出,让你少写注解但契约不丢。代价是"类型来源"变隐式——出问题时得学会用 IDE 的"跳转到类型定义"追链。
// 旧写法:每个环节手写类型,噪音多 function toIds(items: Array<{ id: number }>): number[] { return items.map((it: { id: number }) => it.id); } // 新写法:让推断接管,契约仍在(鼠标悬停能看到 number[]) const toIds = (items: Array<{ id: number }>) => items.map(it => it.id); const ids = toIds([{ id: 1 }, { id: 2 }]); // 推断为 number[]
输出层面,两者的 ids 都是 number[],但第二段少了两处冗余标注,大型代码库累积下来可读性收益明显。判断准绳不变:推断结果你要"看得懂、追得到",一旦追不到就补显式类型,别迷信自动。
前面那张表给了"开不开 strict"的结论,这里把 strict 族里每个开关单独摊开,方便技术负责人按风险点勾选,而不是一股脑全开把团队吓退:
| 开关 | 作用 | 不开的代价 | 建议 |
|---|---|---|---|
strictNullChecks |
空值显式 | 运行时 undefined.x 崩 |
必开,第二章非空判断的底座 |
noImplicitAny |
禁隐式 any | 契约悄悄漏成 any | 必开,第九章反模式源头 |
strictFunctionTypes |
函数参数逆变检查 | 错误回调类型混进来 | 必开,事件处理最常踩 |
noUncheckedIndexedAccess |
索引访问加 undefined |
拼错键静默拿错值 | 数据表/字典场景开 |
noImplicitReturns |
要求所有分支返回 | 漏返回得到 undefined |
多人项目开 |
exactOptionalPropertyTypes |
可选属性不等于 undefined 赋值 |
把"可有可无"和"显式空"混为一谈 | 契约洁癖项目开 |
操作:别一上来全开。先 strict: true 跑全量,把红浪记进 baseline;再挑 noUncheckedIndexedAccess 这类高收益低风险项单独加。结果:团队看到的是"逐步变稳"而不是"一次性红崩"。解读:严格度是工程节奏问题,勾选顺序比勾选数量更重要。
类型生态另一条暗线,是"检查"和"运行"的边界在模糊。早年 TS 只能编译到 JS 再跑;现在若干运行时直接吃 TS,类型检查前移到执行入口,配合 tsc --noEmit 的双层卡点,契约从"能跑就行"变成"跑前就验"。
// 趋势:运行入口直接接收 TS,类型在加载期被校验 // 伪示意:loader 在 import 时先过一遍类型边界 import type { Config } from "./config"; const cfg: Config = loadConfig(); // 若字段缺失,启动即报错而非业务跑到一半才炸 // 这类"启动即契约"会把更多 bug 挡在进程门外,类似建筑交付前的结构验收
该跟进的:标准语法(??、可选链、顶层 await)、模板字面量类型、运行时校验一体化(zod 思路)。该观望的:把业务流程硬塞进类型层当"类型体操"——那会吃掉阅读成本,且新人难以接手,第五章方法论的刹车长期有效。判断准绳依旧是那句话:"它编译后是不是标准 JS"。
.d.ts 产出对外 SDK 最怕消费方为你的类型付出编译代价。第七章、第九章都强调过 skipLibCheck,这里给库作者一个最小可跑骨架,确保你产出的类型"消费方零负担":
// tsconfig.json 节选(库模式) // { // "compilerOptions": { // "strict": true, // "declaration": true, // 输出 .d.ts,消费方拿到契约 // "emitDeclarationOnly": true,// 只出类型,运行时代码由打包器负责 // "skipLibCheck": true, // 不替消费方检查依赖类型,省时 // "noUnusedLocals": true // 顺手清掉死代码 // }, // "include": ["src"] // } // 消费方拿到的是干净契约,例如: // export declare function formatPrice(amount: number, currency: string): string; // 他无需装你的任何 devDependencies 就能享受类型提示
背景:库作者常忽略"消费方编译成本"。操作:开 declaration + skipLibCheck 友好,类型自包含、不依赖内部类型再导出。结果:消费方 tsc 不卡在你的类型上。解读:类型即契约也意味着"你的契约不该成为别人的负担",这是库生态的礼貌底线。
上面每一条建议都有本册对应落脚点,避免"决策层悬空":
// @ts-check 即可,不必上工程化。把"选型结论"和"具体落点"绑在一起,技术负责人拿去就能排期,而不是只拿到一句"该上 TS"。
declaration + skipLibCheck 友好,别让消费方为你的类型慢。十章走完,从"契约是什么"(一)、"怎么判定"(二)、"语法载体"(三)、"团队落地"(四)、"类型可编程"(五)、"编译器原理"(六)、"框架集成"(七)、"架构运用"(八)、"性能与排错"(九)到"沿革与选型"(十)——主线始终没变:类型是你和编译器、和同事、和未来自己签下的契约。它把错误挡在编译期,把意图写进签名,把重构变成可负担的事。
不追求覆盖每个语法,那会淹没主线。真正要带走的,是"写代码前先想契约"这个习惯。当你习惯如此,TypeScript 才真正开始替你干活。
⚠️ 别把"未来类型能力更强"理解成"该把一切都搬进类型层"。表达力越强越要克制,类型复杂度吃掉的阅读成本也是真实成本,第五章方法论的刹车长期有效。
💡 技术负责人落地 TS,第一优先级不是教语法,而是把第四章的工程化(CI 卡类型 + baseline 控错误)立住——环境顺了,团队才愿意写严格代码。