Skip to content
On this page

TypeScript 高级类型

TypeScript 的高级类型功能允许我们创建更复杂、更灵活的类型,从而提高代码的类型安全性。

交叉类型(Intersection Types)

交叉类型是将多个类型合并为一个类型,这让我们能够把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。

typescript
interface ErrorHandling {
  success: boolean;
  error?: { message: string };
}

interface ArtworksData {
  artworks: { title: string }[];
}

interface ArtistsData {
  artists: { name: string }[];
}

// 这些接口被组合,以同时包含所需的全部功能
type ArtworksResponse = ArtworksData & ErrorHandling;
type ArtistsResponse = ArtistsData & ErrorHandling;

const handleArtistsResponse = (response: ArtistsResponse) => {
  if (response.error) {
    console.error(response.error.message);
    return;
  }

  console.log(response.artists);
};

// 组合多个类型
interface Person {
  name: string;
}

interface Contact {
  phone: string;
}

interface Address {
  address: string;
}

type PersonContact = Person & Contact & Address;

const person: PersonContact = {
  name: "John Doe",
  phone: "123-456-7890",
  address: "123 Main St"
};

联合类型(Union Types)

联合类型表示一个值可以是几种类型之一,使用 | 分隔每个类型。

typescript
function formatId(id: number | string) {
  // 联合类型需要类型守卫
  if (typeof id === 'number') {
    return id.toString();
  } else {
    return id;
  }
}

// 联合类型在数组中的使用
let items: (string | number)[] = [1, 'hello', 2, 'world'];

// 联合类型与字面量类型
type Status = 'active' | 'inactive' | 'pending';
type Priority = 1 | 2 | 3 | 4 | 5;

function updateStatus(status: Status) {
  console.log(`Status updated to: ${status}`);
}

类型守卫(Type Guards)

类型守卫是一种在运行时检查值的类型的方法,确保在特定代码块中类型的安全性。

typescript
// typeof 类型守卫
function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
    return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}

// instanceof 类型守卫
interface Padder {
  getPaddingString(): string;
}

class SpaceRepeatingPadder implements Padder {
  constructor(private numSpaces: number) {}
  getPaddingString() {
    return Array(this.numSpaces + 1).join(" ");
  }
}

class StringPadder implements Padder {
  constructor(private value: string) {}
  getPaddingString() {
    return this.value;
  }
}

function getRandomPadder() {
  return Math.random() < 0.5 ?
    new SpaceRepeatingPadder(4) :
    new StringPadder("  ");
}

let padder: Padder = getRandomPadder();

if (padder instanceof SpaceRepeatingPadder) {
  padder.numSpaces; // 在这个分支中,TypeScript 知道这是 SpaceRepeatingPadder
}

// in 操作符类型守卫
type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ('swim' in animal) {
    return animal.swim();
  } else {
    return animal.fly();
  }
}

// 自定义类型守卫
function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

function getFood(pet: Fish | Bird) {
  if (isFish(pet)) {
    pet; // 这里 TypeScript 知道 pet 是 Fish
    return "fish food";
  } else {
    pet; // 这里 TypeScript 知道 pet 是 Bird
    return "bird food";
  }
}

可辨识联合(Discriminated Unions)

可辨识联合是一种高级模式,它结合了联合类型和字面量类型。

typescript
interface Square {
  kind: "square";
  size: number;
}

interface Rectangle {
  kind: "rectangle";
  width: number;
  height: number;
}

interface Circle {
  kind: "circle";
  radius: number;
}

type Shape = Square | Rectangle | Circle;

function area(s: Shape): number {
  // 使用可辨识联合,TypeScript 可以推断出每种情况
  switch (s.kind) {
    case "square":
      return s.size * s.size;
    case "rectangle":
      return s.width * s.height;
    case "circle":
      return Math.PI * s.radius ** 2;
  }
}

// 确保处理了所有情况
function getArea(s: Shape): number {
  switch (s.kind) {
    case "square":
      return s.size * s.size;
    case "rectangle":
      return s.width * s.height;
    case "circle":
      return Math.PI * s.radius ** 2;
    default:
      // 如果添加了新的形状类型,这里会报错
      const _exhaustiveCheck: never = s;
      return _exhaustiveCheck;
  }
}

索引类型(Index Types)

索引类型允许我们表示对象中属性的类型依赖于另一个值。

typescript
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
  return names.map(n => o[n]);
}

interface Person {
  name: string;
  age: number;
}

const person: Person = {
  name: "Jarid",
  age: 35
};

const values: string[] = pluck(person, ['name']); // string[]
const values2: (string | number)[] = pluck(person, ['name', 'age']); // (string | number)[]

// 索引访问操作符
interface Map<T> {
  [key: string]: T;
}

let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number

映射类型(Mapped Types)

映射类型基于旧类型创建新类型,通过遍历键的联合来创建新类型。

typescript
// 基础映射类型
interface Person {
  name: string;
  age: number;
  location: string;
}

type ReadonlyPerson = {
  readonly [P in keyof Person]: Person[P];
};

type PartialPerson = {
  [P in keyof Person]?: Person[P];
};

type PersonNullable = {
  [P in keyof Person]: Person[P] | null;
};

// 映射类型工具
// 1. Partial<T> - 将所有属性设置为可选
type T0 = Partial<Person>;

// 2. Required<T> - 将所有属性设置为必需
type T1 = Required<Partial<Person>>;

// 3. Readonly<T> - 将所有属性设置为只读
type T2 = Readonly<Person>;

// 4. Pick<T, K> - 从 T 中选择 K 指定的属性
type T3 = Pick<Person, "name" | "age">;

// 5. Record<K, T> - 构造一个对象类型,其属性名的类型为 K,属性值的类型为 T
type T4 = Record<"x" | "y", Person>;

// 自定义映射类型
type Proxify<T> = {
  [P in keyof T]: { value: T[P], validator: (value: T[P]) => boolean };
};

function setupProxies<T>(obj: T): { [P in keyof T]: { value: T[P], validator: (value: T[P]) => boolean } } {
  const result = {} as any;
  for (const key in obj) {
    result[key] = {
      value: obj[key],
      validator: () => true
    };
  }
  return result;
}

// 映射类型与条件类型的结合
type FunctionPropertyNames<T> = {
  [K in keyof T]: T[K] extends Function ? K : never
}[keyof T];

type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;

type NonFunctionPropertyNames<T> = {
  [K in keyof T]: T[K] extends Function ? never : K
}[keyof T];

type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;

interface Part {
  id: number;
  name: string;
  subparts: Part[];
  updatePart(newName: string): void;
}

type T5 = FunctionPropertyNames<Part>; // "updatePart"
type T6 = NonFunctionPropertyNames<Part>; // "id" | "name" | "subparts"

条件类型(Conditional Types)

条件类型根据类型 T 是否可分配给类型 U 来选择不同的类型。

typescript
// 基础条件类型
type TypeName<T> = 
  T extends string ? "string" :
  T extends number ? "number" :
  T extends boolean ? "boolean" :
  T extends undefined ? "undefined" :
  T extends Function ? "function" :
  "object";

type T0 = TypeName<string>;  // "string"
type T1 = TypeName<"a">;     // "string"
type T2 = TypeName<true>;    // "boolean"
type T3 = TypeName<() => void>;  // "function"
type T4 = TypeName<string[]>;    // "object"

// 分配条件类型
type BoxedValue<T> = { value: T };
type BoxedArray<T> = { array: T[] };
type Boxed<T> = T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;

type T10 = Boxed<string>;        // BoxedValue<string>
type T11 = Boxed<number[]>;      // BoxedArray<number>
type T12 = Boxed<string | number[]>; // BoxedValue<string> | BoxedArray<number>

// 条件类型约束
type Diff<T, U> = T extends U ? never : T;
type Filter<T, U> = T extends U ? T : never;

type T30 = Diff<"a" | "b" | "c", "a" | "e">;  // "b" | "c"
type T31 = Filter<"a" | "b" | "c", "a" | "e">;  // "a"
type T32 = Diff<string | number | (() => void), Function>;  // string | number
type T33 = Filter<string | number | (() => void), Function>;  // () => void

infer 关键字

infer 关键字用于在条件类型中推断类型。

typescript
// 提取函数返回值类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

type T0 = GetReturnType<() => number>;  // number
type T1 = GetReturnType<(x: string) => boolean>;  // boolean
type T2 = GetReturnType<(a: number, b: string) => void>;  // void

// 提取构造函数实例类型
type GetInstanceType<T> = T extends new (...args: any[]) => infer R ? R : any;

class Foo {
  x = 0;
  y = 0;
}

type T3 = GetInstanceType<typeof Foo>;  // Foo

// 提取数组元素类型
type GetArrayElementType<T> = T extends (infer U)[] ? U : T;

type T4 = GetArrayElementType<string[]>;  // string
type T5 = GetArrayElementType<number>;   // number

// 提取 Promise 解包类型
type Unpromisify<T> = T extends Promise<infer U> ? U : T;

type T6 = Unpromisify<Promise<string>>;  // string
type T7 = Unpromisify<number>;           // number

// 复杂的 infer 使用
type ParamType<T> = T extends (param: infer P) => any ? P : T;

type T8 = ParamType<(name: string) => any>;  // string
type T9 = ParamType<(id: number, name: string) => any>;  // never (不匹配)

// 提取参数类型
type GetParameters<T> = T extends (...args: infer P) => any ? P : never;

type T10 = GetParameters<(a: number, b: string) => void>;  // [number, string]

高级类型工具

深度只读类型

typescript
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
};

interface Config {
  apiUrl: string;
  timeout: number;
  nested: {
    retries: number;
    headers: {
      'Content-Type': string;
    };
  };
}

type ReadonlyConfig = DeepReadonly<Config>;

深度部分类型

typescript
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
};

type PartialConfig = DeepPartial<Config>;

排除和提取类型

typescript
// Exclude<T, U> - 从 T 中排除 U
type T0 = Exclude<"a" | "b" | "c", "a">;  // "b" | "c"

// Extract<T, U> - 从 T 中提取 U
type T1 = Extract<"a" | "b" | "c", "a" | "f">;  // "a"

// NonNullable<T> - 从 T 中排除 null 和 undefined
type T2 = NonNullable<string | number | undefined>; // string | number

实用高级类型模式

类型安全的事件系统

typescript
type EventMap = {
  'user:login': { userId: number; timestamp: Date };
  'user:logout': { userId: number; duration: number };
  'app:error': { message: string; stack?: string };
};

type EventKey = keyof EventMap;

class TypedEventEmitter {
  private listeners: { [K in EventKey]?: Array<(event: EventMap[K]) => void> } = {};

  on<K extends EventKey>(event: K, listener: (event: EventMap[K]) => void) {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event]!.push(listener);
  }

  emit<K extends EventKey>(event: K, data: EventMap[K]) {
    const eventListeners = this.listeners[event];
    if (eventListeners) {
      eventListeners.forEach(listener => listener(data));
    }
  }
}

const emitter = new TypedEventEmitter();

emitter.on('user:login', (data) => {
  console.log(`User ${data.userId} logged in at ${data.timestamp}`);
});

emitter.emit('user:login', { 
  userId: 123, 
  timestamp: new Date() 
});

类型安全的 API 客户端

typescript
// 定义 API 端点和返回类型
type ApiEndpoints = {
  '/users': { id: number; name: string; email: string }[];
  '/users/:id': { id: number; name: string; email: string };
  '/posts': { id: number; title: string; content: string }[];
  '/posts/:id': { id: number; title: string; content: string };
};

type Endpoint = keyof ApiEndpoints;

class TypedApiClient {
  async get<K extends Endpoint>(url: K): Promise<ApiEndpoints[K]> {
    const response = await fetch(url);
    return response.json();
  }
}

const client = new TypedApiClient();

// TypeScript 知道返回类型
client.get('/users').then(users => {
  // users 类型为 { id: number; name: string; email: string }[]
  console.log(users[0].name); // 安全访问
});

小结

TypeScript 的高级类型功能提供了强大的类型操作能力,使我们能够创建更精确、更安全的类型定义。通过交叉类型、联合类型、类型守卫、映射类型和条件类型等特性,我们可以构建复杂但类型安全的代码结构。掌握这些高级类型概念对于编写高质量的 TypeScript 代码至关重要。