Skip to content
第 102 / 250 章前端⏱ 10 分钟阅读

第 102 章:高级类型(Advanced Types)

学习目标

  • 掌握交叉类型与联合类型的高级用法
  • 学会类型守卫与区分联合类型
  • 理解 infer 关键字与条件类型
  • 掌握模板字面量类型

一、交叉类型(Intersection Types)

把多个类型合并为一个,全部满足

1.1 基础语法

typescript
type A = { a: string };
type B = { b: number };

// 必须同时有 a 和 b
type AB = A & B;

const obj: AB = {
  a: 'hello',
  b: 123
};

1.2 合并冲突

typescript
// ❌ 同名字段类型不同 → never
type X = { id: string };
type Y = { id: number };

type XY = X & Y;
const id: XY['id'];  // 类型: string & number = never

// ✅ 同名字段类型相同 → 取该类型
type P = { name: string };
type Q = { name: string };
type PQ = P & Q;  // { name: string }

1.3 实战:mixin 模式

typescript
type Loggable = {
  log(message: string): void;
};

type Serializable = {
  serialize(): string;
};

type Storable = Loggable & Serializable;

class Service implements Storable {
  log(message: string) {
    console.log(message);
  }

  serialize() {
    return JSON.stringify(this);
  }
}

二、联合类型(Union Types)

类型可以是其中任意一个

2.1 基础联合

typescript
type Status = 'pending' | 'success' | 'error';

function setStatus(s: Status) {
  // s 是三种之一
}

// 混合类型
type Value = string | number | boolean | null;

2.2 可辨识联合(Discriminated Union)

typescript
// 关键:用 kind/ type 字段做"辨识符"
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; sideLength: number }
  | { kind: 'rectangle'; width: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'square':
      return shape.sideLength ** 2;
    case 'rectangle':
      return shape.width * shape.height;
  }
}

2.3 穷尽性检查

typescript
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; sideLength: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'square':
      return shape.sideLength ** 2;
    default:
      // 兜底:加新成员时这里会报错
      const _exhaustive: never = shape;
      throw new Error(`未知 shape: ${_exhaustive}`);
  }
}

三、类型守卫(Type Guards)

详细的类型守卫见第 100 章,这里补充一些高级用法。

3.1 自定义类型谓词

typescript
interface Fish {
  swim(): void;
  kind: 'fish';
}

interface Bird {
  fly(): void;
  kind: 'bird';
}

function isFish(animal: Fish | Bird): animal is Fish {
  return animal.kind === 'fish';
}

function move(animal: Fish | Bird) {
  if (isFish(animal)) {
    animal.swim();  // ✅ Fish
  } else {
    animal.fly();   // ✅ Bird
  }
}

3.2 类型谓词 + 泛型

typescript
function isNotNull<T>(value: T | null): value is T {
  return value !== null;
}

function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function isArrayOf<T>(
  value: unknown,
  check: (item: unknown) => item is T
): value is T[] {
  return Array.isArray(value) && value.every(check);
}

四、类型收窄(Narrowing)

typescript
function process(value: string | number | null) {
  // 1. 真值收窄
  if (value) {
    // value: string | number
  }

  // 2. typeof
  if (typeof value === 'string') {
    // value: string
  }

  // 3. instanceof
  if (value instanceof Date) {
    // value: Date
  }

  // 4. in
  if (value && typeof value === 'object' && 'name' in value) {
    // value: object & { name: unknown }
  }
}

五、类型别名 vs 接口的选择

typescript
// ✅ 用 type: 联合、交叉、映射、条件
type Status = 'pending' | 'success' | 'error';
type Mixin = A & B;
type Readonly<T> = { readonly [K in keyof T]: T[K] };

// ✅ 用 interface: 对象结构 + 声明合并
interface User {
  name: string;
  age: number;
}

interface User {
  email: string;  // ✅ 自动合并
}

六、索引访问类型

typescript
interface User {
  name: string;
  age: number;
  address: {
    city: string;
    zip: string;
  };
}

type UserName = User['name'];           // string
type UserAge = User['age'];             // number
type UserAddress = User['address'];      // { city: string; zip: string }
type CityType = User['address']['city']; // string

// 联合索引
type Fields = User['name' | 'age'];     // string | number

七、infer 关键字

在条件类型中推断类型,类似类型层面的解构。

7.1 提取函数返回值

typescript
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function foo(): number { return 1; }
type FooReturn = MyReturnType<typeof foo>;  // number

function bar(): string { return 'hi'; }
type BarReturn = MyReturnType<typeof bar>;  // string

7.2 提取参数类型

typescript
type MyParameterType<T> = T extends (arg: infer P) => any ? P : never;

function greet(name: string) { }
type GreetParam = MyParameterType<typeof greet>;  // string

// 提取 Promise 的值类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

type A = UnwrapPromise<Promise<string>>;  // string
type B = UnwrapPromise<number>;           // number

7.3 提取数组元素

typescript
type ElementOf<T> = T extends (infer E)[] ? E : never;

type NumEl = ElementOf<number[]>;     // number
type StrEl = ElementOf<string[]>;     // string

// 从元组中提取
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;

type T1 = First<[number, string, boolean]>;  // number
type T2 = First<[string]>;                   // string
type T3 = First<[]>;                         // never

7.4 提取构造函数参数

typescript
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;

class User {
  constructor(public name: string, public age: number) {}
}

type UserCtor = ConstructorParameters<typeof User>;  // [string, number]

八、模板字面量类型(TS 4.1+)

8.1 基础模板

typescript
type Greeting = `hello ${string}`;
const a: Greeting = 'hello world';      // ✅
const b: Greeting = 'hello typescript'; // ✅
const c: Greeting = 'hi world';         // ❌

// 配合联合
type Color = 'red' | 'blue';
type Size = 'small' | 'large';
type Variant = `${Color}-${Size}`;
const v1: Variant = 'red-small';    // ✅
const v2: Variant = 'red-big';      // ❌

8.2 大小写转换

typescript
type Uppercase<S extends string> = intrinsic;
type Lowercase<S extends string> = intrinsic;
type Capitalize<S extends string> = intrinsic;
type Uncapitalize<S extends string> = intrinsic;

type A = Uppercase<'hello'>;        // 'HELLO'
type B = Lowercase<'HELLO'>;        // 'hello'
type C = Capitalize<'hello'>;       // 'Hello'
type D = Uncapitalize<'Hello'>;     // 'hello'

8.3 实战:事件名

typescript
type EventName = 'click' | 'focus' | 'blur';
type EventHandler = `on${Capitalize<EventName>}`;
// 'onClick' | 'onFocus' | 'onBlur'

function bind(event: EventName, handler: `on${Capitalize<EventName>}`) {
  // ...
}

bind('click', 'onClick');  // ✅
bind('click', 'onclick');  // ❌

九、keyoftypeof

9.1 keyof

typescript
interface User {
  name: string;
  age: number;
}

type UserKeys = keyof User;  // 'name' | 'age'

function get<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

9.2 typeof

typescript
const config = {
  host: 'localhost',
  port: 3000
};

type Config = typeof config;
// { host: string; port: number }

9.3 组合使用

typescript
const colors = {
  red: '#ff0000',
  green: '#00ff00',
  blue: '#0000ff'
};

type ColorKey = keyof typeof colors;  // 'red' | 'green' | 'blue'

function getColor(key: ColorKey): string {
  return colors[key];
}

getColor('red');    // ✅
getColor('yellow'); // ❌

十、实战:状态机的类型设计

typescript
// 1. 事件类型
type Event =
  | { type: 'FETCH' }
  | { type: 'SUCCESS'; data: User[] }
  | { type: 'ERROR'; error: Error }
  | { type: 'RESET' };

// 2. 状态类型
type State =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: User[] }
  | { status: 'error'; error: Error };

// 3. reducer(强类型)
function reducer(state: State, event: Event): State {
  switch (event.type) {
    case 'FETCH':
      return { status: 'loading' };
    case 'SUCCESS':
      return { status: 'success', data: event.data };
    case 'ERROR':
      return { status: 'error', error: event.error };
    case 'RESET':
      return { status: 'idle' };
  }
}

十一、本章小结

要点关键
交叉类型A & B,同时满足所有类型
联合类型A | B,满足其中之一
可辨识联合kind/type 字段做收窄
类型守卫param is Type 自定义谓词
infer在条件类型中推断子类型
模板字面量字符串级别的类型组合
keyof / typeof提取键名或值的类型

动手练习

  1. 可辨识联合:定义 Result<T> 类型({ok: true, value: T}{ok: false, error: Error}),写一个 handle 函数
  2. infer 练习:写一个 MyAwaited<T> 类型,提取 Promise<T> 的内部类型
  3. 模板字面量:定义 ApiEndpoint 类型,接受 /users/users/:id,写一个类型安全的路由函数
  4. keyof 工具:写一个 RequiredKeys<T> 类型,提取 T 中所有必填字段的键名

推荐阅读


下一章第 103 章:映射类型(Mapped Types)

本站基于 VitePress 构建 · 由 Codebook 团队维护