正在加载,请稍候…

TypeScript 高级类型模式:条件类型、映射类型与类型推断

掌握 TypeScript 高级类型模式,包括条件类型、映射类型、模板字面量类型、可辨识联合、infer 关键字以及类型安全的事件发射器,提升代码的类型安全性和

TypeScript Advanced Type Patterns: Conditional Types, Mapped Types, and Inferenc

超越基础 TypeScript

TypeScript 的类型系统是图灵完备的。这些高级模式让你能够在类型层面编码复杂的约束。

TypeScript Advanced Type Patterns: Conditional Types, Mapped Types, and Inferenc illustration

条件类型

// 基础条件类型
type IsString<T> = T extends string ? true : false

type A = IsString<string>  // true
type B = IsString<number>  // false

// 提取类型
type Unwrap<T> = T extends Promise<infer U> ? U : T
type UnwrapArray<T> = T extends Array<infer U> ? U : T

type X = Unwrap<Promise<string>>   // string
type Y = UnwrapArray<number[]>      // number

// 递归条件类型
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}

type Config = DeepReadonly<{
  server: { port: number; host: string }
  db: { url: string; poolSize: number }
}>
// config.server.port = 3000  // Error! readonly

映射类型

// 标准工具类型(来自 lib.d.ts)—— 为学习而复制
type MyPartial<T> = { [K in keyof T]?: T[K] }
type MyRequired<T> = { [K in keyof T]-?: T[K] }  // -? 移除可选性
type MyReadonly<T> = { readonly [K in keyof T]: T[K] }
type MyPick<T, K extends keyof T> = { [P in K]: T[P] }

// 高级:转换值类型
type Stringify<T> = { [K in keyof T]: string }
type Nullable<T> = { [K in keyof T]: T[K] | null }
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}

interface User { id: number; name: string }
type UserGetters = Getters<User>
// { getId: () => number; getName: () => string }

TypeScript Advanced Type Patterns: Conditional Types, Mapped Types, and Inferenc illustration

模板字面量类型

type EventName<T extends string> = `on${Capitalize<T>}`
type Events = EventName<'click' | 'hover' | 'focus'>
// "onClick" | "onHover" | "onFocus"

// 深层键路径
type Paths<T, Prefix extends string = ''> = {
  [K in keyof T & string]:
    T[K] extends object
      ? Paths<T[K], `${Prefix}${K}.`>
      : `${Prefix}${K}`
}[keyof T & string]

type Config = { server: { port: number; host: string }; debug: boolean }
type ConfigPaths = Paths<Config>  // "server.port" | "server.host" | "debug"

可辨识联合

type Result<T, E = Error> =
  | { success: true; data: T }
  | { success: false; error: E }

function divide(a: number, b: number): Result<number, string> {
  if (b === 0) return { success: false, error: 'Division by zero' }
  return { success: true, data: a / b }
}

const result = divide(10, 2)
if (result.success) {
  console.log(result.data)    // TypeScript 知道这是 number
} else {
  console.log(result.error)   // TypeScript 知道这是 string
}

// 更强大:带缩窄的标签联合
type Action =
  | { type: 'INCREMENT'; amount: number }
  | { type: 'DECREMENT'; amount: number }
  | { type: 'RESET' }

function reducer(state: number, action: Action): number {
  switch (action.type) {
    case 'INCREMENT': return state + action.amount  // amount 可用
    case 'DECREMENT': return state - action.amount  // amount 可用
    case 'RESET': return 0                           // 无 amount
  }
}

TypeScript Advanced Type Patterns: Conditional Types, Mapped Types, and Inferenc illustration

infer 关键字

// 提取返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never

// 提取第一个参数
type FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never

// 提取 Promise 值
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T

// 提取组件 props (React)
type PropsOf<T> = T extends React.ComponentType<infer P> ? P : never

// 推断元组元素
type Head<T extends any[]> = T extends [infer H, ...any[]] ? H : never
type Tail<T extends any[]> = T extends [any, ...infer T] ? T : never

type H = Head<[1, 2, 3]>  // 1
type T = Tail<[1, 2, 3]>  // [2, 3]

带类型的构建器模式

type RequiredKeys<T> = {
  [K in keyof T]: undefined extends T[K] ? never : K
}[keyof T]

class QueryBuilder<T extends Record<string, any>, Selected extends keyof T = never> {
  private selectedFields: (keyof T)[] = []
  private conditions: string[] = []

  select<K extends keyof T>(...fields: K[]): QueryBuilder<T, Selected | K> {
    this.selectedFields.push(...fields)
    return this as any
  }

  where(condition: string): this {
    this.conditions.push(condition)
    return this
  }

  build(): string {
    return `SELECT ${this.selectedFields.join(', ')} WHERE ${this.conditions.join(' AND ')}`
  }
}

const query = new QueryBuilder<User>()
  .select('id', 'name')
  .where('active = true')
  .build()

类型安全的事件发射器

type EventMap = Record<string, any[]>

class TypedEventEmitter<Events extends EventMap> {
  private listeners = new Map<keyof Events, Function[]>()

  on<K extends keyof Events>(event: K, listener: (...args: Events[K]) => void): this {
    const existing = this.listeners.get(event) ?? []
    this.listeners.set(event, [...existing, listener])
    return this
  }

  emit<K extends keyof Events>(event: K, ...args: Events[K]): void {
    this.listeners.get(event)?.forEach(listener => listener(...args))
  }
}

type AppEvents = {
  userCreated: [user: User]
  orderPlaced: [order: Order, userId: string]
  error: [message: string, code: number]
}

const emitter = new TypedEventEmitter<AppEvents>()
emitter.on('userCreated', (user) => console.log(user.name))  // user 是 User 类型
emitter.emit('orderPlaced', order, '123')