为什么要学”类型体操”
TypeScript 最被低估的能力,是它能在编译期把大量 Bug 挡在运行前。很多人只用到 string/number 这种基础类型,但真正省心的是泛型、条件类型、映射类型这些”高级玩法”——它们能让你改一个接口字段,所有调用处立刻报红,而不是半夜收到线上报错。
一、联合类型与字面量
type Status = 'pending' | 'running' | 'done';
function render(s: Status) { /* ... */ }
render('done'); // OK
render('error'); // 编译报错:不在联合内
二、泛型:让类型跟着数据走
function first<T>(arr: T[]): T | undefined {
return arr[0];
}
const a = first([1, 2, 3]); // a: number | undefined
const b = first(['x', 'y']); // b: string | undefined
三、条件类型 + infer
条件类型像三元表达式,infer 能从已有类型里”提取”结构:
type ElementType<T> = T extends (infer U)[] ? U : T;
type X = ElementType<number[]>; // number
type Y = ElementType<string>; // string
四、映射类型:批量改造
把已有类型的每个属性统一加 readonly 或改可选:
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
五、内置工具类型够用大半
| 工具类型 | 作用 |
|---|---|
| Partial<T> | 所有属性变可选 |
| Required<T> | 所有属性变必填 |
| Pick<T, K> | 只取部分属性 |
| Record<K, V> | 构造键值映射类型 |
| ReturnType<F> | 提取函数返回值类型 |
六、实战:给 API 响应建类型
interface ApiResp<T> {
code: number;
msg: string;
data: T;
}
type UserResp = ApiResp<{ id: number; name: string }>;
function getUser(): UserResp {
// 返回结构不对,编译期直接报错
return { code: 0, msg: 'ok', data: { id: 1, name: 'Agnes' } };
}
七、判别联合(Discriminated Union)
用同一个字段区分不同类型,配合 switch 让 TS 自动收窄:
type Shape =
| { kind: 'circle'; r: number }
| { kind: 'rect'; w: number; h: number };
function area(s: Shape) {
if (s.kind === 'circle') return Math.PI * s.r ** 2;
return s.w * s.h; // 这里 TS 已知道是 rect
}
小结
类型体操不是炫技,而是把”运行时才发现的错”提前到”写代码时就看见”。从联合类型、泛型入手,再学条件类型和映射类型,你的 TypeScript 会从”加了类型的 JavaScript”进化成真正的类型安全护盾。




