Skip to content

写在前面

这份教程的定位

本文档适用于已经写了两三年 TypeScript,日常业务代码里的类型都能写,但看到三方库源码里那些 infer、递归条件类型、模板字面量类型时会发懵。这份教程就是为这个状态写的:

  • 不重复官方文档。TS Handbook 讲得很好的部分不展开,只给链接。
  • 偏"为什么"而不是"怎么用"。比如 extends 为什么既能做约束又能做判断,satisfies 到底解决了什么问题。
  • 类型编程讲套路不讲题海。把类型编程拆成六个可复用的模式,而不是罗列一百道题的答案。
  • 关键概念可验证。代码块接入了 Twoslash,悬停能看到编译器真实的推导结果。

TypeScript 到底是什么

一句话:TypeScript = JavaScript + 一个只在编译期存在的类型系统 + 一个能把它编译回 JS 的编译器。

它有三个彼此独立的身份,混在一起看会永远搞不清:

身份做什么在哪个阶段
类型检查器分析你的代码,找出类型错误编译期(和 IDE 实时)
转译器把新语法降级成目标环境的 JS编译期
语言服务给 IDE 提供补全、跳转、重命名编辑时

关键结论:类型在运行前会被完全擦除。TS 不会给运行时加任何校验,也不会改变生成的 JS 语义(enumnamespace 是仅有的两个例外,它们会生成真实代码)。

ts
// 你写的
interface User { name: string }
function greet(u: User): string {
  return `hi ${u.name}`
}

// tsc 输出(target: ES2020)
function greet(u) {
  return `hi ${u.name}`;
}

所以 if (typeof x === 'string') 这种检查在 TS 里依然要你手写。TS 只是让你在写的时候就被提醒。

三个世界

理解类型层的关键,是分清这三个层次:

ts
// ① 值层:运行时真实存在的数据
const 
user
= {
name
: 'ada',
age
: 36 }
// ② 类型层:编译期的形状描述 type
User
= typeof
user
// ③ 类型构造函数(泛型):把类型映射成类型的函数 type
Nullable
<
T
> =
T
| null
type
MaybeUser
=
Nullable
<
User
>

泛型就是类型层的函数<T> 是参数,= 右边是返回值,Nullable<User> 是调用。理解这一点之后,类型编程篇的所有内容都会顺很多。

阅读路线

如果你时间有限,按这个顺序读:

  1. 编译流程 —— 先搞清 tsc / Babel / swc 谁负责什么,否则工程配置会一直靠猜
  2. tsconfig 逐项精讲 —— 每个开关在做什么、默认值有什么坑
  3. 收窄与判别联合 —— 日常收益最高的单一技巧
  4. 条件类型与分发 —— 类型编程的门槛
  5. 六大套路 —— 类型编程的总纲
  6. 调试与类型测试 —— 卡住时的救命工具

Twoslash 怎么用

教程里带 Twoslash 标记的代码块是可以交互的:

  • 鼠标悬停在带虚线下划线的标识符上,会弹出它的真实类型
  • //^? 这一行会被替换成上一行表达式的推导结果
  • 带红色波浪线的地方是故意保留的错误,用来演示什么写法会报错
ts
type 
Todo
= {
title
: string;
done
: boolean }
type
Keys
= keyof
Todo
const
arr
= [1, 2, 3] as
const
type
Elem
= typeof
arr
[number]

想自己改着玩时,每节末尾一般会给出一个可以直接打开的 Playground 链接。

基于 VitePress 与 Twoslash 构建