用户工具

站点工具


typescript:第七章泛型

第七章:泛型

本章概述

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

7.1 泛型基础

7.1.1 为什么需要泛型

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

```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 类型,没有代码提示 ``` 使用泛型解决: ```typescript 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 类型(自动推断) ```

7.1.2 泛型语法

```typescript 泛型函数 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;
}

} ```

7.2 泛型函数

7.2.1 基本泛型函数

```typescript 简单的泛型函数 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; } ``` ==== 7.2.2 多个类型参数 ==== ```typescript 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] ```

7.2.3 泛型函数类型

```typescript 定义泛型函数类型 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; ```

7.3 泛型接口

7.3.1 泛型接口定义

```typescript 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 }; ``` ==== 7.3.2 泛型接口方法 ==== ```typescript 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; } } ``` ===== 7.4 泛型类 ===== ==== 7.4.1 基本泛型类 ==== ```typescript 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”); ``` ==== 7.4.2 泛型类约束 ==== ```typescript 要求类型必须有 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 ```

7.5 泛型约束

7.5.1 使用接口约束

```typescript 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 属性 ``` ==== 7.5.2 使用 keyof 约束 ==== ```typescript 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 ``` ==== 7.5.3 多重约束 ==== ```typescript 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 ``` ===== 7.6 泛型工具类型 ===== ==== 7.6.1 内置泛型工具 ==== ```typescript 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] ```

7.6.2 自定义泛型工具

```typescript 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]; }; ``` ===== 7.7 高级泛型技巧 ===== ==== 7.7.1 条件类型 ==== ```typescript 基本语法: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 ``` ==== 7.7.2 infer 关键字 ==== ```typescript 提取数组元素类型 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; ``` ==== 7.7.3 映射类型 ==== ```typescript 基础映射类型 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 } ``` ===== 7.8 泛型默认参数 ===== ```typescript 为泛型参数设置默认值 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; } }; ``` ===== 7.9 本章小结 ===== 本章我们学习了: 1. 泛型基础 - 类型参数、泛型语法 2. 泛型函数 - 函数泛型、多个类型参数 3. 泛型接口 - 接口定义泛型、泛型方法 4. 泛型类 - 类泛型、泛型约束 5. 泛型约束 - 接口约束、keyof 约束、多重约束 6. 泛型工具 - 内置工具和自定义工具 7. 高级技巧 - 条件类型、infer、映射类型 ===== 7.10 练习题 ===== ==== 练习 1:泛型数据存储 ==== 实现一个泛型的 KeyValueStore 类,要求: - 支持 set(key, value)、get(key)、has(key)、delete(key) 方法 - 键必须是字符串,值是泛型 - 支持获取所有键、所有值 - 支持清空存储 <details> <summary>参考答案</summary> ```typescript 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”] ```

</details>

练习 2:泛型工具类型实现

实现以下泛型工具类型:

```typescript 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; 实现 ```

<details> <summary>参考答案</summary>

```typescript 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 } ``` </details> ==== 练习 3:泛型 API 客户端 ==== 实现一个类型安全的 HTTP 客户端: ```typescript 要求: 1. 支持泛型请求/响应类型 2. 支持 GET、POST、PUT、DELETE 方法 3. 支持请求/响应拦截器 4. 类型安全的路径参数 5. 自动推断响应类型 ``` <details> <summary>参考答案</summary> ```typescript 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 
});

} ```

</details>

扩展阅读

typescript/第七章泛型.txt · 最后更改: 127.0.0.1