TypeScript 类型体操:从入门到实用技巧

为什么要学”类型体操”

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”进化成真正的类型安全护盾。

上一篇 Redis 持久化选型:RDB vs AOF 深度对比
下一篇 Jenkins vs GitLab CI vs GitHub Actions 选型指南