📚 现代前端开发 目录
阶段2:工程化
第 12 / 35 课

TypeScript进阶:泛型与类型体操

掌握TypeScript的类型编程

📖 核心概念

💻 代码实现

泛型与条件类型 ✅ typescript
// 泛型约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

// 条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false

// infer - 在条件类型中推断类型
type ReturnTypeOf<T> = T extends (...args: any[]) => infer R ? R : never;
type PromiseValue<T> = T extends Promise<infer V> ? V : T;

// 递归展开Promise
type Awaited<T> = T extends Promise<infer V> ? Awaited<V> : T;
type DeepPromise = Promise<Promise<Promise<string>>>;
type Result = Awaited<DeepPromise>; // string

// 实用条件类型
type FunctionParams<T> = T extends (...args: infer P) => any ? P : never;
type ConstructorParams<T> = T extends new (...args: infer P) => any ? P : never;

// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never;
type StrOrNumArr = ToArray<string | number>; // string[] | number[]

// 阻止分布式:用元组包裹
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type StrOrNumArr2 = ToArrayNonDist<string | number>; // (string | number)[]
映射类型与类型体操 ✅ typescript
// 映射类型
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Partial<T> = { [K in keyof T]?: T[K] };
type Required<T> = { [K in keyof T]-?: T[K] };
type Nullable<T> = { [K in keyof T]: T[K] | null };

// 键重映射 (TS 4.1+)
type Getters<T> = {
  [K in keyof T as \`get\${Capitalize<string & K>}\`]: () => T[K];
};
interface Person { name: string; age: number; }
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number }

// 模板字面量类型
type EventName<T extends string> = \`on\${Capitalize<T>}\`;
type ClickEvent = EventName<'click'>; // "onClick"

// 深层只读
type DeepReadonly<T> = T extends Function
  ? T
  : T extends object
  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : T;

// 深层Partial
type DeepPartial<T> = T extends Function
  ? T
  : T extends Array<infer U>
  ? Array<DeepPartial<U>>
  : T extends object
  ? { [K in keyof T]?: DeepPartial<T[K]> }
  : T;

// 提取路由参数类型
type ExtractParams<T extends string> =
  T extends \`\${infer Start}:\${infer Param}/\${infer Rest}\`
  ? { [K in Param | keyof ExtractParams<Rest>]: string }
  : T extends \`\${infer Start}:\${infer Param}\`
  ? { [K in Param]: string }
  : {};

type RouteParams = ExtractParams<'/users/:userId/posts/:postId'>;
// { userId: string; postId: string }

// 类型安全的事件系统
type EventMap = {
  login: { userId: string; timestamp: Date };
  logout: { userId: string };
  error: { code: number; message: string };
};

type EventHandler<T extends keyof EventMap> = (payload: EventMap[T]) => void;

class TypeSafeEmitter<T extends Record<string, any>> {
  private handlers = new Map<keyof T, Set<Function>>();

  on<K extends keyof T>(event: K, handler: (payload: T[K]) => void) {
    if (!this.handlers.has(event)) this.handlers.set(event, new Set());
    this.handlers.get(event)!.add(handler);
    return () => this.off(event, handler);
  }

  off<K extends keyof T>(event: K, handler: (payload: T[K]) => void) {
    this.handlers.get(event)?.delete(handler);
  }

  emit<K extends keyof T>(event: K, payload: T[K]) {
    this.handlers.get(event)?.forEach(h => h(payload));
  }
}

const emitter = new TypeSafeEmitter<EventMap>();
emitter.on('login', ({ userId }) => { /* TypeScript自动推断类型 */ });
emitter.emit('error', { code: 404, message: 'Not found' });

🔍 类型体操实战技巧

类型体操的核心思路:先用具体类型推导,再泛化。善用infer在条件类型中提取类型、使用递归类型处理嵌套结构、利用模板字面量类型处理字符串模式。推荐练习Type Challenges题库,从Easy到Hard逐步提升。

🎯 练习任务

🏆 成就解锁
类型体操冠军 — 掌握TypeScript高级类型编程,让类型系统为你工作