TypeScript 学习路线 MOC
从会写 JavaScript 到能用 TypeScript 设计可靠业务契约的七阶段学习路线,覆盖类型心智、收窄、泛型、运行时边界、React 与工程化。
#type / moc
#status / growing
#tech / dev
#resource / typescript
[!info] related notes
- 所属总入口:TypeScript MOC
- 对象介绍:TypeScript
- JavaScript 前置:JavaScript 学习路线
- React 应用:React 学习路线
- 项目实战:BodySense MOC
TypeScript 学习路线 MOC
这条路线解决什么问题
TypeScript 的难点不是给变量补 string,而是学会把“不可能状态”排除在业务代码之外。本路线面向已经熟悉常用 JavaScript 写法、但还没有形成类型设计心智的人。
[!abstract] 路线结论 先分清 JavaScript 运行时和 TypeScript 编译期,再掌握推断、对象类型、联合与收窄;随后用可辨识联合和泛型表达业务关系,并学习外部数据必须运行时校验。最后进入 React、跨端契约、tsconfig 和 monorepo。
针对已有 JS 基础的使用方式
| 当前情况 | 建议 |
|---|---|
| 熟悉数组方法、函数和接口写法 | 快速通过阶段一,但完成每个“完成标准” |
经常使用 as 解决报错 | 重点学习阶段二、三和五 |
| 会写 React 组件 | 阶段三后进入阶段六,同时继续阶段四、五 |
| 只会看类型,不会设计类型 | 从 BodySense StreamEvent 契约开始反向阅读 |
| 被 tsconfig、编辑器红线困扰 | 完成语言主线后再进阶段七 |
难度标记
- L1 入门 · L2 基础巩固 · L3 核心类型设计 · L4 工程/跨端 · L5 编译器与进阶
- 源码:否 / 建议 / 需要 —— 是否值得对照 TypeScript Handbook、编译器实现或项目真实源码
依赖关系
flowchart TD
S1["阶段一 编译期心智"] --> S2["阶段二 对象与组合类型"]
S2 --> S3["阶段三 收窄与状态建模"]
S3 --> S4["阶段四 泛型与类型变换"]
S3 --> S5["阶段五 运行时边界"]
S4 --> S6["阶段六 React 与业务契约"]
S5 --> S6
S6 --> S7["阶段七 工具链与大型工程"]
阶段一:建立编译期类型心智
目标:知道 TypeScript 能保证什么、不能保证什么;优先依赖推断而不是到处手写类型。
完成标准:
- 能解释类型擦除和 JavaScript 运行时的关系
- 能区分注解、推断和断言
- 能说明
any、unknown、never、void的职责
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 1.1 | TypeScript | TypeScript 在工具链中的位置 | L1 | 否 |
| 1.2 | TypeScript 类型推断与类型标注 | 何时让编译器推断,何时显式标注 | L2 | 否 |
| 1.3 | TypeScript 中的 unknown 与 any | 安全未知值与放弃检查的差异 | L2 | 否 |
| 1.4 | TypeScript 静态类型与运行时校验 | 类型为什么不能验证网络数据 | L3 | 建议 |
| 1.5 | TypeScript 速记 | 高频类型与工具类型快速入口 | L1 | 否 |
阶段二:对象与组合类型
目标:用对象类型、联合、交叉、可选属性与索引访问准确表达数据形状。
完成标准:
- 能在
type和interface间按能力而非口号选择 - 能解释可选属性与
undefined的差异 - 能读懂
T[K]、keyof T和Record<K, V>
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 2.1 | TypeScript 对象类型、type 与 interface | 如何描述和组合对象形状 | L2 | 否 |
| 2.2 | TypeScript 中的 strictNullChecks | 空值如何进入类型系统 | L2 | 否 |
| 2.3 | TypeScript 中的 exactOptionalPropertyTypes | 缺失属性与显式 undefined 的边界 | L3 | 否 |
| 2.4 | TypeScript 中的 noUncheckedIndexedAccess | 索引读取为什么可能不存在 | L3 | 否 |
| 2.5 | TypeScript 工具类型示例 | Partial/Pick/Omit/Record 如何变换对象 | L2 | 否 |
阶段三:收窄与业务状态建模
目标:让控制流帮助编译器排除不可能状态,而不是用断言压掉错误。
完成标准:
- 能用
typeof、in、判空和类型谓词收窄unknown - 能用共同判别字段设计业务状态
- 能用
never做穷尽检查
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 3.1 | TypeScript 联合类型、收窄与类型守卫 | 控制流如何缩小类型范围 | L3 | 建议 |
| 3.2 | TypeScript 可辨识联合与穷尽检查 | 如何把业务状态设计为有限集合 | L3 | 建议 |
| 3.3 | TypeScript 的 as const、satisfies 与类型断言 | 保留字面量信息而不欺骗编译器 | L3 | 建议 |
| 3.4 | TypeScript 中的 useUnknownInCatchVariables | catch 中为什么应先确认错误形状 | L2 | 否 |
阶段四:泛型与类型变换
目标:表达输入和输出之间的关系,而不是把泛型当作“任意类型占位符”。
完成标准:
- 能写带约束的泛型函数
- 能解释
keyof、索引访问与映射类型如何协作 - 能读懂基础条件类型与
infer
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 4.1 | TypeScript 泛型、keyof 与索引访问 | 如何表达参数之间的类型关系 | L3 | 建议 |
| 4.2 | TypeScript 高级类型 | 条件类型、映射类型与 infer 总览 | L4 | 建议 |
| 4.3 | TypeScript 中的 strictFunctionTypes | 函数参数兼容性为何特殊 | L4 | 建议 |
| 4.4 | TypeScript 结构化类型与函数兼容性 | “形状兼容”如何影响赋值和回调 | L4 | 建议 |
阶段五:运行时边界与模块
目标:把 API、JSON、localStorage、环境变量等外部输入视为不可信数据。
完成标准:
- 能从
unknown开始解析外部数据 - 能说明类型断言和运行时校验的根本差异
- 能区分类型导入与运行时导入
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 5.1 | TypeScript 静态类型与运行时校验 | 网络边界如何建立可信类型 | L3 | 建议 |
| 5.2 | Zod 运行时 Schema 校验 | Schema 如何把 unknown 解析为可信输出 | L3 | 建议 |
| 5.3 | TypeScript 中的 import type 与普通 import | 类型依赖是否进入运行时 | L2 | 否 |
| 5.4 | TypeScript 中的 .d.ts 声明文件 | 没有源码的库如何提供类型 | L3 | 否 |
| 5.5 | TypeScript 中的全局声明与全局扩展 | 全局类型如何安全扩展 | L4 | 否 |
阶段六:React 与真实业务契约
目标:在组件、Hooks、Reducer、Context 和跨端事件中设计稳定类型。
完成标准:
- 能正确标注 props、children、事件与 ref
- 能用可辨识联合设计 reducer action
- 能解释服务端数据为什么仍要运行时校验
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 6.1 | React 中的 TypeScript 组件与 Hook 类型 | React 边界如何保留推断 | L3 | 建议 |
| 6.2 | React 类型化包装组件与多态组件 | 如何继承原生或第三方组件 props 并保留 ref | L3 | 建议 |
| 6.3 | useReducer | action 联合如何驱动状态机 | L3 | 建议 |
| 6.4 | Context 与状态管理边界 | Context 值和 action 如何设计 | L3 | 建议 |
| 6.5 | 前后端 DTO 同步 | 跨端契约如何避免漂移 | L4 | 建议 |
| 6.6 | BodySense MOC | 在真实三语言契约中练习 | L4 | 需要 |
阶段七:工具链与大型工程
目标:理解一个 TypeScript program 如何被编辑器、编译器、bundler 和 monorepo 工具共同处理。
完成标准:
- 能分清
tsc、tsserver、ESLint 与 bundler - 能解释
module和moduleResolution - 能读懂项目引用与 monorepo 类型边界
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 7.1 | TypeScript、tsc、tsconfig 与 tsserver 的关系 | 四个工具层如何协作 | L3 | 否 |
| 7.2 | tsconfig 使用详解 | program 边界和检查规则在哪里定义 | L3 | 否 |
| 7.3 | module 与 moduleResolution 的关系 | 输出格式与查找算法如何分工 | L4 | 建议 |
| 7.4 | TypeScript 中的 nodenext 与 bundler | 不同宿主如何选解析策略 | L4 | 否 |
| 7.5 | TypeScript 中的 references | 大型项目如何分割构建图 | L4 | 否 |
| 7.6 | TypeScript Monorepo 中源码引用与构建引用 | 源码消费和构建产物如何选择 | L5 | 建议 |
如果只抓主干,优先这 8 个主题
- 类型推断与标注
- 对象类型、type 与 interface
- unknown 与 any
- 联合与收窄
- 可辨识联合与穷尽检查
- 泛型、keyof 与索引访问
- as const、satisfies 与断言
- 静态类型与运行时校验
0 到 1 的完成标准
- 能在不使用
any和不滥用as的情况下解析一个外部 JSON - 能用可辨识联合表达 loading/success/error 或事件状态
- 能给对象访问辅助函数写出
K extends keyof T - 能解释类型为什么在运行时不存在
- 能为 React reducer 设计 action 联合并做穷尽检查
- 能读懂 BodySense
StreamEventBase的三个泛型参数 - 能定位一次报错来自类型系统、tsconfig、编辑器还是 bundler
第一条项目练习路径
- 阅读 BodySense
packages/contracts/src/stream-events.ts。 - 找出字面量类型、泛型、
unknown、never和可辨识联合。 - 阅读前端
activeTurnReducer.ts,观察event.type如何收窄。 - 找出
consultationService.ts中从 JSON 直接断言为StreamEvent的位置。 - 为该边界设计运行时解析器和失败测试。