第七章:泛型

泛型(Generics)是 TypeScript 最强大的特性之一,它允许我们编写灵活的、可重用的代码,同时保持类型安全。本章将深入讲解泛型的概念、使用和高级技巧。

在没有泛型的情况下,我们可能需要这样写代码:

// 为每种类型写单独的函数
function identityNumber(arg: number): number {
  return arg;
}
 
function identityString(arg: string): string {
  return arg;
}
 
function identityAny(arg: any): any {
  return arg;
}
 
// 使用 any 的问题:丢失类型信息
const result = identityAny("hello");
// result 是 any 类型,没有代码提示

使用泛型解决:

function identity<T>(arg: T): T {
  return arg;
}
 
// 使用
const num = identity<number>(42);      // number 类型
const str = identity<string>("hello"); // string 类型
 
// 类型推断
const inferred = identity(true);       // boolean 类型(自动推断)
// 泛型函数
function genericFunction<T>(arg: T): T {
  return arg;
}
 
// 泛型箭头函数
const genericArrow = <T>(arg: T): T => arg;
 
// 泛型接口
interface GenericIdentityFn<T> {
  (arg: T): T;
}
 
// 泛型类
class GenericClass<T> {
  value: T;
  constructor(value: T) {
    this.value = value;
  }
}
// 简单的泛型函数
function echo<T>(value: T): T {
  return value;
}
 
// 泛型数组参数
function loggingIdentity<T>(arg: T[]): T[] {
  console.log(arg.length);
  return arg;
}
 
// 或者使用 Array<T>
function loggingIdentity2<T>(arg: Array<T>): Array<T> {
  console.log(arg.length);
  return arg;
}
function pair<T, U>(first: T, second: U): [T, U] {
  return [first, second];
}
 
const result = pair<string, number>("age", 25);
// 结果类型: [string, number]
 
// 交换函数
function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}
 
const swapped = swap(["hello", 42]);
// swapped 类型: [number, string]
// 定义泛型函数类型
type GenericFn<T> = (arg: T) => T;
type MapFn<T, U> = (item: T) => U;
 
// 使用
const identityFn: GenericFn<number> = (x) => x;
const stringLength: MapFn<string, number> = (s) => s.length;
interface KeyValuePair<K, V> {
  key: K;
  value: V;
}
 
// 使用
const stringNumberPair: KeyValuePair<string, number> = {
  key: "age",
  value: 25
};
 
const numberBooleanPair: KeyValuePair<number, boolean> = {
  key: 1,
  value: true
};
interface Collection<T> {
  add(item: T): void;
  remove(item: T): boolean;
  get(index: number): T | undefined;
  size(): number;
}
 
class ArrayCollection<T> implements Collection<T> {
  private items: T[] = [];
 
  add(item: T): void {
    this.items.push(item);
  }
 
  remove(item: T): boolean {
    const index = this.items.indexOf(item);
    if (index > -1) {
      this.items.splice(index, 1);
      return true;
    }
    return false;
  }
 
  get(index: number): T | undefined {
    return this.items[index];
  }
 
  size(): number {
    return this.items.length;
  }
}
class Stack<T> {
  private items: T[] = [];
 
  push(item: T): void {
    this.items.push(item);
  }
 
  pop(): T | undefined {
    return this.items.pop();
  }
 
  peek(): T | undefined {
    return this.items[this.items.length - 1];
  }
 
  isEmpty(): boolean {
    return this.items.length === 0;
  }
 
  size(): number {
    return this.items.length;
  }
 
  clear(): void {
    this.items = [];
  }
}
 
// 使用
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop());  // 2
 
const stringStack = new Stack<string>();
stringStack.push("hello");
stringStack.push("world");
// 要求类型必须有 length 属性
interface HasLength {
  length: number;
}
 
class LengthLogger<T extends HasLength> {
  logLength(item: T): void {
    console.log(`Length: ${item.length}`);
  }
}
 
const logger = new LengthLogger<string>();
logger.logLength("hello");  // Length: 5
 
const arrayLogger = new LengthLogger<number[]>();
arrayLogger.logLength([1, 2, 3]);  // Length: 3
interface HasName {
  name: string;
}
 
function greet<T extends HasName>(obj: T): string {
  return `Hello, ${obj.name}!`;
}
 
// 使用
greet({ name: "Alice" });  // OK
greet({ name: "Bob", age: 25 });  // OK,额外的属性可以
// greet({ age: 25 });  // Error: 缺少 name 属性
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
 
const person = {
  name: "Alice",
  age: 25,
  email: "alice@example.com"
};
 
const name = getProperty(person, "name");    // string 类型
const age = getProperty(person, "age");      // number 类型
// const invalid = getProperty(person, "gender");  // Error: "gender" 不是有效 key
interface Printable {
  print(): void;
}
 
interface Loggable {
  log(): void;
}
 
// 要求 T 同时实现两个接口
function process<T extends Printable & Loggable>(item: T): void {
  item.print();
  item.log();
}
 
class Document implements Printable, Loggable {
  print(): void {
    console.log("Printing...");
  }
 
  log(): void {
    console.log("Logging...");
  }
}
 
process(new Document());  // OK
// Partial<T> - 所有属性变为可选
interface User {
  name: string;
  age: number;
  email: string;
}
 
type PartialUser = Partial<User>;
// 等价于 { name?: string; age?: number; email?: string }
 
// Required<T> - 所有属性变为必需
type RequiredUser = Required<Partial<User>>;
 
// Readonly<T> - 所有属性变为只读
type ReadonlyUser = Readonly<User>;
 
// Record<K, T> - 创建键值对类型
type PageNames = "home" | "about" | "contact";
type PageInfo = { title: string; path: string };
type Pages = Record<PageNames, PageInfo>;
 
// Pick<T, K> - 从 T 中选取部分属性
type UserPreview = Pick<User, "name" | "email">;
// 等价于 { name: string; email: string }
 
// Omit<T, K> - 从 T 中排除部分属性
type UserWithoutEmail = Omit<User, "email">;
// 等价于 { name: string; age: number }
 
// 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> - 排除 null 和 undefined
type T2 = NonNullable<string | number | undefined | null>;  // string | number
 
// ReturnType<T> - 获取函数返回类型
type Fn = () => { x: number; y: number };
type FnReturn = ReturnType<Fn>;  // { x: number; y: number }
 
// Parameters<T> - 获取函数参数类型
type FnParams = Parameters<(x: number, y: string) => void>;
// [number, string]
// Nullable<T> - 使类型可为 null
type Nullable<T> = T | null;
 
// DeepPartial<T> - 深度可选
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
 
// DeepReadonly<T> - 深度只读
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
 
// KeysOfType<T, U> - 获取指定类型的键
type KeysOfType<T, U> = {
  [K in keyof T]: T[K] extends U ? K : never;
}[keyof T];
 
interface Person {
  name: string;
  age: number;
  email: string;
  isActive: boolean;
}
 
type StringKeys = KeysOfType<Person, string>;  // "name" | "email"
type NumberKeys = KeysOfType<Person, number>;  // "age"
 
// Mutable<T> - 移除 readonly
type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};
// 基本语法:T extends U ? X : Y
type IsString<T> = T extends string ? true : false;
 
type T1 = IsString<string>;   // true
type T2 = IsString<number>;   // false
 
// 实际应用:根据类型选择不同的处理
type MessageOf<T> = T extends { message: infer M } ? M : never;
 
interface Email {
  message: string;
}
 
interface Dog {
  bark(): void;
}
 
type EmailMessage = MessageOf<Email>;  // string
type DogMessage = MessageOf<Dog>;      // never
// 提取数组元素类型
type ElementType<T> = T extends (infer E)[] ? E : never;
 
type Numbers = ElementType<number[]>;  // number
type Strings = ElementType<string[]>;  // string
 
// 提取 Promise 的返回值类型
type PromiseType<T> = T extends Promise<infer R> ? R : never;
 
type P1 = PromiseType<Promise<string>>;  // string
type P2 = PromiseType<Promise<number>>;  // number
 
// 提取函数返回类型
type ReturnType2<T> = T extends (...args: any[]) => infer R ? R : never;
 
// 提取函数参数类型
type Parameters2<T> = T extends (...args: infer P) => any ? P : never;
// 基础映射类型
type Readonly2<T> = {
  readonly [P in keyof T]: T[P];
};
 
// 添加可选修饰符
type Optional<T> = {
  [P in keyof T]?: T[P];
};
 
// 移除可选修饰符
type Required2<T> = {
  [P in keyof T]-?: T[P];
};
 
// 重映射键名
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 Filter<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K];
};
 
type StringProps = Filter<Person, string>;  // { name: string }
// 为泛型参数设置默认值
function createArray<T = string>(length: number, value: T): T[] {
  return Array(length).fill(value);
}
 
const stringArray = createArray(3, "x");  // string[]
const numberArray = createArray<number>(3, 0);  // number[]
 
// 接口默认泛型
interface GenericInterface<T = string> {
  value: T;
  process(): T;
}
 
// 使用默认类型
const defaultInterface: GenericInterface = {
  value: "hello",
  process() { return this.value; }
};
 
// 指定类型
const numberInterface: GenericInterface<number> = {
  value: 42,
  process() { return this.value * 2; }
};

本章我们学习了:

1. 泛型基础 - 类型参数、泛型语法

2. 泛型函数 - 函数泛型、多个类型参数

3. 泛型接口 - 接口定义泛型、泛型方法

4. 泛型类 - 类泛型、泛型约束

5. 泛型约束 - 接口约束、keyof 约束、多重约束

6. 泛型工具 - 内置工具和自定义工具

7. 高级技巧 - 条件类型、infer、映射类型

实现一个泛型的 KeyValueStore 类,要求:

  1. 支持 set(key, value)、get(key)、has(key)、delete(key) 方法
  2. 键必须是字符串,值是泛型
  3. 支持获取所有键、所有值
  4. 支持清空存储

参考答案

class KeyValueStore<T> {
  private store: Map<string, T> = new Map();
 
  set(key: string, value: T): void {
    this.store.set(key, value);
  }
 
  get(key: string): T | undefined {
    return this.store.get(key);
  }
 
  has(key: string): boolean {
    return this.store.has(key);
  }
 
  delete(key: string): boolean {
    return this.store.delete(key);
  }
 
  keys(): string[] {
    return Array.from(this.store.keys());
  }
 
  values(): T[] {
    return Array.from(this.store.values());
  }
 
  entries(): Array<[string, T]> {
    return Array.from(this.store.entries());
  }
 
  clear(): void {
    this.store.clear();
  }
 
  size(): number {
    return this.store.size;
  }
 
  // 泛型方法:查找符合条件的值
  find(predicate: (value: T) => boolean): T | undefined {
    for (const [, value] of this.store) {
      if (predicate(value)) {
        return value;
      }
    }
    return undefined;
  }
 
  // 泛型方法:过滤
  filter(predicate: (value: T) => boolean): KeyValueStore<T> {
    const result = new KeyValueStore<T>();
    for (const [key, value] of this.store) {
      if (predicate(value)) {
        result.set(key, value);
      }
    }
    return result;
  }
 
  // 泛型方法:映射
  map<U>(transform: (value: T) => U): KeyValueStore<U> {
    const result = new KeyValueStore<U>();
    for (const [key, value] of this.store) {
      result.set(key, transform(value));
    }
    return result;
  }
}
 
// 使用
const userStore = new KeyValueStore<{ name: string; age: number }>();
userStore.set("user1", { name: "Alice", age: 25 });
userStore.set("user2", { name: "Bob", age: 30 });
 
const user = userStore.get("user1");
console.log(user?.name);  // "Alice"
 
// 查找年龄大于 25 的用户
const olderUser = userStore.find(u => u.age > 25);
console.log(olderUser?.name);  // "Bob"
 
// 获取所有用户名称
const nameStore = userStore.map(u => u.name);
console.log(nameStore.values());  // ["Alice", "Bob"]

实现以下泛型工具类型:

// 1. DeepPartial - 深度 Partial
type DeepPartial<T> = any;  // 实现
 
// 2. DeepRequired - 深度 Required
type DeepRequired<T> = any;  // 实现
 
// 3. Flatten - 将嵌套数组扁平化为一层
type Flatten<T> = any;  // 实现
 
// 4. TupleToUnion - 将元组转换为联合类型
type TupleToUnion<T> = any;  // 实现
 
// 5. UnionToIntersection - 将联合类型转换为交叉类型
type UnionToIntersection<T> = any;  // 实现

参考答案

// 1. DeepPartial
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
 
// 2. DeepRequired
type DeepRequired<T> = {
  [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
};
 
// 3. Flatten
type Flatten<T> = T extends (infer U)[] ? U : T;
 
// 4. TupleToUnion
type TupleToUnion<T extends readonly any[]> = T[number];
 
// 5. UnionToIntersection
type UnionToIntersection<T> = 
  (T extends any ? (x: T) => void : never) extends 
  (x: infer R) => void ? R : never;
 
// 测试
interface Nested {
  a: {
    b: {
      c: string;
    };
    d: number;
  };
  e: boolean;
}
 
type PartialNested = DeepPartial<Nested>;
// { a?: { b?: { c?: string }; d?: number }; e?: boolean }
 
type Flattened = Flatten<string[]>;  // string
type Flattened2 = Flatten<number[][]>;  // number[]
 
type Union = TupleToUnion<["a", "b", "c"]>;  // "a" | "b" | "c"
 
type Intersection = UnionToIntersection<{ a: 1 } | { b: 2 }>;
// { a: 1 } & { b: 2 }

实现一个类型安全的 HTTP 客户端:

// 要求:
// 1. 支持泛型请求/响应类型
// 2. 支持 GET、POST、PUT、DELETE 方法
// 3. 支持请求/响应拦截器
// 4. 类型安全的路径参数
// 5. 自动推断响应类型

参考答案

// HTTP 方法类型
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
 
// 请求配置
interface RequestConfig<T = any> {
  method?: HttpMethod;
  headers?: Record<string, string>;
  body?: T;
  params?: Record<string, string | number>;
}
 
// 响应类型
interface ApiResponse<T> {
  data: T;
  status: number;
  statusText: string;
  headers: Record<string, string>;
}
 
// 拦截器类型
type RequestInterceptor = (config: RequestConfig) => RequestConfig | Promise<RequestConfig>;
type ResponseInterceptor<T> = (response: ApiResponse<T>) => ApiResponse<T> | Promise<ApiResponse<T>>;
type ErrorInterceptor = (error: any) => any;
 
// API 客户端
class ApiClient {
  private baseURL: string;
  private requestInterceptors: RequestInterceptor[] = [];
  private responseInterceptors: Array<ResponseInterceptor<any>> = [];
  private errorInterceptors: ErrorInterceptor[] = [];
 
  constructor(baseURL: string) {
    this.baseURL = baseURL;
  }
 
  // 添加拦截器
  addRequestInterceptor(interceptor: RequestInterceptor): void {
    this.requestInterceptors.push(interceptor);
  }
 
  addResponseInterceptor<T>(interceptor: ResponseInterceptor<T>): void {
    this.responseInterceptors.push(interceptor);
  }
 
  addErrorInterceptor(interceptor: ErrorInterceptor): void {
    this.errorInterceptors.push(interceptor);
  }
 
  // 核心请求方法
  async request<TResponse, TRequest = any>(
    url: string,
    config: RequestConfig<TRequest> = {}
  ): Promise<ApiResponse<TResponse>> {
    // 应用请求拦截器
    let finalConfig = config;
    for (const interceptor of this.requestInterceptors) {
      finalConfig = await interceptor(finalConfig);
    }
 
    // 构建 URL
    let fullUrl = this.baseURL + url;
    if (finalConfig.params) {
      const params = new URLSearchParams();
      for (const [key, value] of Object.entries(finalConfig.params)) {
        params.append(key, String(value));
      }
      fullUrl += "?" + params.toString();
    }
 
    // 发送请求
    try {
      const response = await fetch(fullUrl, {
        method: finalConfig.method || "GET",
        headers: {
          "Content-Type": "application/json",
          ...finalConfig.headers
        },
        body: finalConfig.body ? JSON.stringify(finalConfig.body) : undefined
      });
 
      const data = await response.json();
 
      let apiResponse: ApiResponse<TResponse> = {
        data,
        status: response.status,
        statusText: response.statusText,
        headers: Object.fromEntries(response.headers.entries())
      };
 
      // 应用响应拦截器
      for (const interceptor of this.responseInterceptors) {
        apiResponse = await interceptor(apiResponse);
      }
 
      return apiResponse;
    } catch (error) {
      // 应用错误拦截器
      for (const interceptor of this.errorInterceptors) {
        await interceptor(error);
      }
      throw error;
    }
  }
 
  // 便捷方法
  async get<T>(url: string, params?: Record<string, string | number>): Promise<T> {
    const response = await this.request<T>(url, { method: "GET", params });
    return response.data;
  }
 
  async post<T, D = any>(url: string, data: D): Promise<T> {
    const response = await this.request<T, D>(url, { method: "POST", body: data });
    return response.data;
  }
 
  async put<T, D = any>(url: string, data: D): Promise<T> {
    const response = await this.request<T, D>(url, { method: "PUT", body: data });
    return response.data;
  }
 
  async delete<T>(url: string): Promise<T> {
    const response = await this.request<T>(url, { method: "DELETE" });
    return response.data;
  }
}
 
// 使用示例
interface User {
  id: number;
  name: string;
  email: string;
}
 
interface CreateUserRequest {
  name: string;
  email: string;
}
 
const api = new ApiClient("https://api.example.com");
 
// 添加拦截器
api.addRequestInterceptor((config) => {
  config.headers = {
    ...config.headers,
    "Authorization": "Bearer token-123"
  };
  return config;
});
 
// 类型安全的 API 调用
async function example() {
  // GET 请求 - 自动推断返回类型为 User[]
  const users = await api.get<User[]>("/users");
  console.log(users[0].name);  // 类型安全
 
  // POST 请求
  const newUser = await api.post<User, CreateUserRequest>("/users", {
    name: "Alice",
    email: "alice@example.com"
  });
  console.log(newUser.id);  // 类型安全
 
  // 带查询参数
  const filteredUsers = await api.get<User[]>("/users", { 
    page: 1, 
    limit: 10 
  });
}

该主题尚不存在

您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。

  • typescript/第七章_泛型.txt
  • 最后更改: 2026/03/09 15:24
  • 张叶安