====== 第七章:泛型 ====== ===== 本章概述 ===== 泛型(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(arg: T): T { return arg; } // 使用 const num = identity(42); // number 类型 const str = identity("hello"); // string 类型 // 类型推断 const inferred = identity(true); // boolean 类型(自动推断) ``` ==== 7.1.2 泛型语法 ==== ```typescript // 泛型函数 function genericFunction(arg: T): T { return arg; } // 泛型箭头函数 const genericArrow = (arg: T): T => arg; // 泛型接口 interface GenericIdentityFn { (arg: T): T; } // 泛型类 class GenericClass { value: T; constructor(value: T) { this.value = value; } } ``` ===== 7.2 泛型函数 ===== ==== 7.2.1 基本泛型函数 ==== ```typescript // 简单的泛型函数 function echo(value: T): T { return value; } // 泛型数组参数 function loggingIdentity(arg: T[]): T[] { console.log(arg.length); return arg; } // 或者使用 Array function loggingIdentity2(arg: Array): Array { console.log(arg.length); return arg; } ``` ==== 7.2.2 多个类型参数 ==== ```typescript function pair(first: T, second: U): [T, U] { return [first, second]; } const result = pair("age", 25); // 结果类型: [string, number] // 交换函数 function swap(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]]; } const swapped = swap(["hello", 42]); // swapped 类型: [number, string] ``` ==== 7.2.3 泛型函数类型 ==== ```typescript // 定义泛型函数类型 type GenericFn = (arg: T) => T; type MapFn = (item: T) => U; // 使用 const identityFn: GenericFn = (x) => x; const stringLength: MapFn = (s) => s.length; ``` ===== 7.3 泛型接口 ===== ==== 7.3.1 泛型接口定义 ==== ```typescript interface KeyValuePair { key: K; value: V; } // 使用 const stringNumberPair: KeyValuePair = { key: "age", value: 25 }; const numberBooleanPair: KeyValuePair = { key: 1, value: true }; ``` ==== 7.3.2 泛型接口方法 ==== ```typescript interface Collection { add(item: T): void; remove(item: T): boolean; get(index: number): T | undefined; size(): number; } class ArrayCollection implements Collection { 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 { 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(); numberStack.push(1); numberStack.push(2); console.log(numberStack.pop()); // 2 const stringStack = new Stack(); stringStack.push("hello"); stringStack.push("world"); ``` ==== 7.4.2 泛型类约束 ==== ```typescript // 要求类型必须有 length 属性 interface HasLength { length: number; } class LengthLogger { logLength(item: T): void { console.log(`Length: ${item.length}`); } } const logger = new LengthLogger(); logger.logLength("hello"); // Length: 5 const arrayLogger = new LengthLogger(); arrayLogger.logLength([1, 2, 3]); // Length: 3 ``` ===== 7.5 泛型约束 ===== ==== 7.5.1 使用接口约束 ==== ```typescript interface HasName { name: string; } function greet(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(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(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 - 所有属性变为可选 interface User { name: string; age: number; email: string; } type PartialUser = Partial; // 等价于 { name?: string; age?: number; email?: string } // Required - 所有属性变为必需 type RequiredUser = Required>; // Readonly - 所有属性变为只读 type ReadonlyUser = Readonly; // Record - 创建键值对类型 type PageNames = "home" | "about" | "contact"; type PageInfo = { title: string; path: string }; type Pages = Record; // Pick - 从 T 中选取部分属性 type UserPreview = Pick; // 等价于 { name: string; email: string } // Omit - 从 T 中排除部分属性 type UserWithoutEmail = Omit; // 等价于 { name: string; age: number } // Exclude - 从 T 中排除可赋值给 U 的类型 type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c" // Extract - 从 T 中提取可赋值给 U 的类型 type T1 = Extract<"a" | "b" | "c", "a" | "f">; // "a" // NonNullable - 排除 null 和 undefined type T2 = NonNullable; // string | number // ReturnType - 获取函数返回类型 type Fn = () => { x: number; y: number }; type FnReturn = ReturnType; // { x: number; y: number } // Parameters - 获取函数参数类型 type FnParams = Parameters<(x: number, y: string) => void>; // [number, string] ``` ==== 7.6.2 自定义泛型工具 ==== ```typescript // Nullable - 使类型可为 null type Nullable = T | null; // DeepPartial - 深度可选 type DeepPartial = { [P in keyof T]?: T[P] extends object ? DeepPartial : T[P]; }; // DeepReadonly - 深度只读 type DeepReadonly = { readonly [P in keyof T]: T[P] extends object ? DeepReadonly : T[P]; }; // KeysOfType - 获取指定类型的键 type KeysOfType = { [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; // "name" | "email" type NumberKeys = KeysOfType; // "age" // Mutable - 移除 readonly type Mutable = { -readonly [P in keyof T]: T[P]; }; ``` ===== 7.7 高级泛型技巧 ===== ==== 7.7.1 条件类型 ==== ```typescript // 基本语法:T extends U ? X : Y type IsString = T extends string ? true : false; type T1 = IsString; // true type T2 = IsString; // false // 实际应用:根据类型选择不同的处理 type MessageOf = T extends { message: infer M } ? M : never; interface Email { message: string; } interface Dog { bark(): void; } type EmailMessage = MessageOf; // string type DogMessage = MessageOf; // never ``` ==== 7.7.2 infer 关键字 ==== ```typescript // 提取数组元素类型 type ElementType = T extends (infer E)[] ? E : never; type Numbers = ElementType; // number type Strings = ElementType; // string // 提取 Promise 的返回值类型 type PromiseType = T extends Promise ? R : never; type P1 = PromiseType>; // string type P2 = PromiseType>; // number // 提取函数返回类型 type ReturnType2 = T extends (...args: any[]) => infer R ? R : never; // 提取函数参数类型 type Parameters2 = T extends (...args: infer P) => any ? P : never; ``` ==== 7.7.3 映射类型 ==== ```typescript // 基础映射类型 type Readonly2 = { readonly [P in keyof T]: T[P]; }; // 添加可选修饰符 type Optional = { [P in keyof T]?: T[P]; }; // 移除可选修饰符 type Required2 = { [P in keyof T]-?: T[P]; }; // 重映射键名 type Getters = { [K in keyof T as `get${Capitalize}`]: () => T[K]; }; interface Person { name: string; age: number; } type PersonGetters = Getters; // { // getName: () => string; // getAge: () => number; // } // 过滤属性 type Filter = { [K in keyof T as T[K] extends U ? K : never]: T[K]; }; type StringProps = Filter; // { name: string } ``` ===== 7.8 泛型默认参数 ===== ```typescript // 为泛型参数设置默认值 function createArray(length: number, value: T): T[] { return Array(length).fill(value); } const stringArray = createArray(3, "x"); // string[] const numberArray = createArray(3, 0); // number[] // 接口默认泛型 interface GenericInterface { value: T; process(): T; } // 使用默认类型 const defaultInterface: GenericInterface = { value: "hello", process() { return this.value; } }; // 指定类型 const numberInterface: GenericInterface = { 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) 方法 - 键必须是字符串,值是泛型 - 支持获取所有键、所有值 - 支持清空存储
参考答案 ```typescript class KeyValueStore { private store: Map = 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 { const result = new KeyValueStore(); for (const [key, value] of this.store) { if (predicate(value)) { result.set(key, value); } } return result; } // 泛型方法:映射 map(transform: (value: T) => U): KeyValueStore { const result = new KeyValueStore(); 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"] ```
==== 练习 2:泛型工具类型实现 ==== 实现以下泛型工具类型: ```typescript // 1. DeepPartial - 深度 Partial type DeepPartial = any; // 实现 // 2. DeepRequired - 深度 Required type DeepRequired = any; // 实现 // 3. Flatten - 将嵌套数组扁平化为一层 type Flatten = any; // 实现 // 4. TupleToUnion - 将元组转换为联合类型 type TupleToUnion = any; // 实现 // 5. UnionToIntersection - 将联合类型转换为交叉类型 type UnionToIntersection = any; // 实现 ```
参考答案 ```typescript // 1. DeepPartial type DeepPartial = { [P in keyof T]?: T[P] extends object ? DeepPartial : T[P]; }; // 2. DeepRequired type DeepRequired = { [P in keyof T]-?: T[P] extends object ? DeepRequired : T[P]; }; // 3. Flatten type Flatten = T extends (infer U)[] ? U : T; // 4. TupleToUnion type TupleToUnion = T[number]; // 5. UnionToIntersection type UnionToIntersection = (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; // { a?: { b?: { c?: string }; d?: number }; e?: boolean } type Flattened = Flatten; // string type Flattened2 = Flatten; // number[] type Union = TupleToUnion<["a", "b", "c"]>; // "a" | "b" | "c" type Intersection = UnionToIntersection<{ a: 1 } | { b: 2 }>; // { a: 1 } & { b: 2 } ```
==== 练习 3:泛型 API 客户端 ==== 实现一个类型安全的 HTTP 客户端: ```typescript // 要求: // 1. 支持泛型请求/响应类型 // 2. 支持 GET、POST、PUT、DELETE 方法 // 3. 支持请求/响应拦截器 // 4. 类型安全的路径参数 // 5. 自动推断响应类型 ```
参考答案 ```typescript // HTTP 方法类型 type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; // 请求配置 interface RequestConfig { method?: HttpMethod; headers?: Record; body?: T; params?: Record; } // 响应类型 interface ApiResponse { data: T; status: number; statusText: string; headers: Record; } // 拦截器类型 type RequestInterceptor = (config: RequestConfig) => RequestConfig | Promise; type ResponseInterceptor = (response: ApiResponse) => ApiResponse | Promise>; type ErrorInterceptor = (error: any) => any; // API 客户端 class ApiClient { private baseURL: string; private requestInterceptors: RequestInterceptor[] = []; private responseInterceptors: Array> = []; private errorInterceptors: ErrorInterceptor[] = []; constructor(baseURL: string) { this.baseURL = baseURL; } // 添加拦截器 addRequestInterceptor(interceptor: RequestInterceptor): void { this.requestInterceptors.push(interceptor); } addResponseInterceptor(interceptor: ResponseInterceptor): void { this.responseInterceptors.push(interceptor); } addErrorInterceptor(interceptor: ErrorInterceptor): void { this.errorInterceptors.push(interceptor); } // 核心请求方法 async request( url: string, config: RequestConfig = {} ): Promise> { // 应用请求拦截器 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 = { 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(url: string, params?: Record): Promise { const response = await this.request(url, { method: "GET", params }); return response.data; } async post(url: string, data: D): Promise { const response = await this.request(url, { method: "POST", body: data }); return response.data; } async put(url: string, data: D): Promise { const response = await this.request(url, { method: "PUT", body: data }); return response.data; } async delete(url: string): Promise { const response = await this.request(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("/users"); console.log(users[0].name); // 类型安全 // POST 请求 const newUser = await api.post("/users", { name: "Alice", email: "alice@example.com" }); console.log(newUser.id); // 类型安全 // 带查询参数 const filteredUsers = await api.get("/users", { page: 1, limit: 10 }); } ```
===== 扩展阅读 ===== - [[TypeScript:第八章_高级类型|下一章:高级类型]] - [[https://www.typescriptlang.org/docs/handbook/2/generics.html|TypeScript 官方文档 - 泛型]]