TypeScript 类型系统深度解析 从基础到高级类型体操 如何解决复杂业务场景中的类型推断与泛型难题
TypeScript 的类型系统是其最强大的特性之一,它不仅仅是静态类型检查的工具,更是一种能够表达复杂逻辑和业务约束的编程语言。理解 TypeScript 的类型系统,尤其是从基础到高级的类型体操(Type Gymnastics),对于在复杂业务场景中编写健壮、可维护的代码至关重要。本文将深入解析 TypeScript 的类型系统,涵盖基础类型、高级类型、类型推断、泛型编程以及如何在复杂业务场景中应用这些知识解决实际问题。
1. TypeScript 类型系统基础
在深入高级类型体操之前,我们需要先巩固 TypeScript 类型系统的基础知识。这些基础概念是理解更复杂类型操作的前提。
1.1 基础类型与注解
TypeScript 提供了 JavaScript 原始类型的对应类型:number、string、boolean、null、undefined、symbol、bigint。此外,还有 object 类型表示非原始类型。
// 基础类型注解 let count: number = 5; let name: string = "Alice"; let isDone: boolean = false; let obj: object = { key: "value" }; // 数组类型 let numbers: number[] = [1, 2, 3]; let strings: Array<string> = ["a", "b", "c"]; // 元组类型 let tuple: [string, number] = ["hello", 10]; // 严格顺序和长度 // 枚举类型 enum Color { Red, Green, Blue } let c: Color = Color.Green; 1.2 接口与类型别名
接口(interface)和类型别名(type)是定义复杂类型的主要方式。
// 接口定义对象形状 interface Person { name: string; age: number; greet(phrase: string): void; } // 类型别名可以定义更广泛的类型 type StringOrNumber = string | number; type Point = { x: number; y: number }; // 实现接口 const user: Person = { name: "Bob", age: 30, greet(phrase) { console.log(`${phrase} ${this.name}`); } }; 1.3 联合类型与交叉类型
联合类型(|)和交叉类型(&)提供了类型组合的能力。
// 联合类型 function padLeft(value: string, padding: string | number) { if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; } return padding + value; } // 交叉类型 interface Employee { employeeId: string; department: string; } interface Person { name: string; age: number; } type Staff = Employee & Person; const staff: Staff = { employeeId: "E001", department: "Engineering", name: "Charlie", age: 28 }; 1.4 类型推断与类型断言
TypeScript 能够根据上下文自动推断类型,但在必要时可以使用类型断言。
// 类型推断 let message = "Hello"; // 推断为 string 类型 const numbers = [1, 2, 3]; // 推断为 number[] // 类型断言 let someValue: any = "this is a string"; let strLength: number = (someValue as string).length; // 另一种语法 let strLength2: number = (<string>someValue).length; 2. 高级类型与类型体操
当基础类型无法满足复杂需求时,TypeScript 提供了高级类型特性,这些特性被称为”类型体操”,它们允许我们通过类型运算来创建极其精确和灵活的类型。
2.1 映射类型(Mapped Types)
映射类型允许我们通过遍历一个类型的所有属性来创建新类型。TypeScript 内置了许多映射类型,如 Partial、Readonly、Pick、Record 等。
// 内置映射类型示例 interface Todo { title: string; description: string; completed: boolean; } // 将所有属性变为可选 type PartialTodo = Partial<Todo>; // 将所有属性变为只读 type ReadonlyTodo = Readonly<Todo>; // 选择指定属性 type TodoPreview = Pick<Todo, "title" | "completed">; // 创建键值对映射 type TodoRecord = Record<number, Todo>; // 自定义映射类型 type Nullable<T> = { [P in keyof T]: T[P] | null; }; type NullableTodo = Nullable<Todo>; 2.2 条件类型(Conditional Types)
条件类型允许我们根据类型关系进行类型选择,类似于三元运算符。
// 基础条件类型 type IsString<T> = T extends string ? true : false; type A = IsString<string>; // true type B = IsString<number>; // false // 分布式条件类型 type Flatten<T> = T extends any[] ? T[number] : T; type C = Flatten<[1, 2, 3]>; // number type D = Flatten<string>; // string // 实际应用:函数返回类型推断 type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; function getString(): string { return "hello"; } function getNumber(): number { return 42; } type StringReturn = ReturnType<typeof getString>; // string type NumberReturn = ReturnType<typeof getNumber>; // number 2.3 递归类型(Recursive Types)
递归类型允许类型引用自身,这对于树形结构、嵌套对象等非常有用。
// 树形结构 interface TreeNode<T> { value: T; children?: TreeNode<T>[]; } // 深度嵌套的扁平化 type DeepPartial<T> = { [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]; }; interface NestedObject { a: { b: { c: number; d: string; }; e: boolean; }; f: string; } type NestedPartial = DeepPartial<NestedObject>; // 无限递归类型(需要启用 --noImplicitAny) type Json = | string | number | boolean | null | Json[] | { [key: string]: Json }; const validJson: Json = { name: "Alice", age: 30, hobbies: ["reading", "coding"], address: { city: "Beijing", coordinates: [116.4074, 39.9042] } }; 2.4 类型推断与 infer 关键字
infer 关键字在条件类型中用于声明类型变量,让 TypeScript 自动推断类型。
// 推断函数参数类型 type FirstParameter<T> = T extends (...args: infer P) => any ? P[0] : never; function example(a: string, b: number): void {} type First = FirstParameter<typeof example>; // string // 推断构造函数参数 type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never; class ExampleClass { constructor(public name: string, public age: number) {} } type Params = ConstructorParameters<typeof ExampleClass>; // [string, number] // 推断 Promise 的解析类型 type UnwrapPromise<T> = T extends Promise<infer U> ? U : T; type PromiseType = UnwrapPromise<Promise<string>>; // string 3. 泛型编程:解决复杂业务场景的核心
泛型是 TypeScript 类型系统的核心,它允许我们编写可重用、类型安全的代码,同时保持灵活性。
3.1 泛型基础与约束
// 基础泛型函数 function identity<T>(arg: T): T { return arg; } const output = identity<string>("myString"); const numberOutput = identity(123); // 类型推断 // 泛型约束 interface Lengthwise { length: number; } function loggingIdentity<T extends Lengthwise>(arg: T): T { console.log(arg.length); return arg; } loggingIdentity({ length: 10, value: 3 }); // OK // loggingIdentity(3); // 错误:number 没有 length 属性 // 在接口中使用泛型 interface GenericIdentityFn<T> { (arg: T): T; } const myIdentity: GenericIdentityFn<number> = identity; 3.2 默认泛型参数
// 默认类型参数 interface PaginatedResponse<T = any> { data: T[]; total: number; page: number; } // 使用默认参数 const defaultResponse: PaginatedResponse = { data: [1, 2, 3], total: 3, page: 1 }; // 指定类型参数 interface User { id: number; name: string; } const userResponse: PaginatedResponse<User> = { data: [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" } ], total: 2, page: 1 }; 3.3 泛型约束中的类型参数
// 在泛型约束中使用类型参数 function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const x = { a: 1, b: 2, c: 3 }; getProperty(x, "a"); // OK // getProperty(x, "m"); // 错误:m 不是 x 的属性 // 使用泛型创建工厂函数 function create<T>(c: { new(): T }): T { return new c(); } class Greeter { greeting: string; constructor() { this.greeting = "Hello"; } } const greeter = create<Greeter>(Greeter); 3.4 高级泛型模式
// 泛型与条件类型结合 type ExtractType<T, U> = T extends U ? T : never; type Numeric = ExtractType<string | number | boolean, number>; // number // 泛型与映射类型结合 function partial<T>(obj: T): Partial<T> { // 实现细节... return obj; } // 泛型递归 type DeepReadonly<T> = { readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]; }; interface DeepObject { a: { b: { c: number; }; }; } type ReadonlyDeepObject = DeepReadonly<DeepObject>; // a.b.c 变为只读 4. 复杂业务场景中的类型推断与泛型难题
在实际业务开发中,我们经常遇到复杂的类型推断和泛型问题。本节将通过具体场景展示如何解决这些问题。
4.1 场景一:API 响应类型处理
在处理 API 响应时,我们经常需要根据不同的端点和参数推断出不同的返回类型。
// 定义 API 端点映射 interface ApiEndpoints { "/users": { id: number; name: string }[]; "/users/{id}": { id: number; name: string; email: string }; "/posts": { id: number; title: string }[]; "/posts/{id}": { id: number; title: string; content: string }; } // 通用 API 请求函数 async function apiRequest<T extends keyof ApiEndpoints>( endpoint: T, params?: Record<string, any> ): Promise<ApiEndpoints[T]> { // 实际实现中,这里会进行 fetch 调用 const response = await fetch(endpoint, { params }); return response.json(); } // 使用示例 async function example() { const users = await apiRequest("/users"); // 推断为 { id: number; name: string }[] const user = await apiRequest("/users/1"); // 推断为 { id: number; name: string; email: string } const posts = await apiRequest("/posts"); // 推断为 { id: number; title: string }[] } 4.2 场景二:表单验证与类型安全
在复杂表单场景中,我们需要根据验证规则推断出正确的字段类型。
// 验证器类型 type Validator<T> = { validate: (value: any) => { valid: boolean; value: T }; rules: any[]; }; // 创建验证器工厂 function createValidator<T>(rules: any[]): Validator<T> { return { validate: (value: any) => { // 验证逻辑... return { valid: true, value: value as T }; }, rules }; } // 表单字段配置 const formConfig = { name: createValidator<string>([{ required: true }, { maxLength: 50 }]), age: createValidator<number>([{ required: true }, { min: 0 }, { max: 120 }]), email: createValidator<string>([{ required: true }, { pattern: /^S+@S+.S+$/ }]), birthDate: createValidator<Date>([{ required: true }]) }; // 推断表单值类型 type FormValues = { [K in keyof typeof formConfig]: ReturnType<typeof formConfig[K]["validate"]>["value"] }; // 使用表单值 const formValue: FormValues = { name: "Alice", age: 30, email: "alice@example.com", birthDate: new Date("1990-01-01") }; 4.3 场景三:Redux-like 状态管理
在状态管理库中,我们需要根据 Action 的类型推断出 Reducer 的状态变化。
// 定义 Action 类型 type Action<T extends string, P = undefined> = { type: T; payload?: P; }; // 定义 Action 类型集合 type AppActions = | Action<"SET_USER", { id: number; name: string }> | Action<"UPDATE_AGE", number> | Action<"RESET">; // Reducer 类型 type Reducer<S, A extends Action<string, any>> = (state: S, action: A) => S; // 创建 Reducer 工厂 function createReducer<S, A extends Action<string, any>>( initialState: S, handlers: { [K in A["type"]]: (state: S, payload: Extract<A, { type: K }>["payload"]) => S } ): Reducer<S, A> { return (state: S = initialState, action: A) => { const handler = handlers[action.type as A["type"]]; if (handler) { return handler(state, (action as any).payload); } return state; }; } // 使用示例 interface UserState { id: number | null; name: string; age: number; } const initialState: UserState = { id: null, name: "", age: 0 }; const userReducer = createReducer<UserState, AppActions>(initialState, { SET_USER: (state, payload) => ({ ...state, id: payload.id, name: payload.name }), UPDATE_AGE: (state, payload) => ({ ...state, age: payload }), RESET: () => initialState }); // 使用 Reducer const newState = userReducer(initialState, { type: "SET_USER", payload: { id: 1, name: "Alice" } }); 4.4 场景四:插件系统与动态类型
在插件系统中,我们需要处理动态加载的模块,同时保持类型安全。
// 插件接口定义 interface Plugin<T extends Record<string, any> = {}> { name: string; version: string; init: (context: T) => void; execute: (data: any) => any; } // 插件注册表 type PluginRegistry = { [K: string]: Plugin<any>; }; // 插件管理器 class PluginManager<T extends PluginRegistry> { private plugins: Partial<T> = {}; register<K extends keyof T>(name: K, plugin: T[K]): void { this.plugins[name] = plugin; } getPlugin<K extends keyof T>(name: K): T[K] | undefined { return this.plugins[name]; } execute<K extends keyof T>(name: K, data: Parameters<T[K]["execute"]>[0]): ReturnType<T[K]["execute"]> | undefined { const plugin = this.getPlugin(name); return plugin ? plugin.execute(data) : undefined; } } // 定义具体插件 const analyticsPlugin: Plugin<{ userId: string }> = { name: "analytics", version: "1.0.0", init(context) { console.log(`Analytics initialized for user: ${context.userId}`); }, execute(data) { return { tracked: true, data }; } }; const loggingPlugin: Plugin = { name: "logging", version: "1.0.0", init() { console.log("Logging initialized"); }, execute(data) { console.log("Log:", data); return data; } }; // 使用插件管理器 type MyPlugins = { analytics: typeof analyticsPlugin; logging: typeof loggingPlugin; }; const manager = new PluginManager<MyPlugins>(); manager.register("analytics", analyticsPlugin); manager.register("logging", loggingPlugin); // 类型安全的插件调用 const analyticsResult = manager.execute("analytics", { event: "page_view" }); const loggingResult = manager.execute("logging", "test message"); 5. 高级类型体操技巧
在处理极其复杂的类型需求时,需要使用一些高级的类型体操技巧。
5.1 类型守卫与类型收窄
// 自定义类型守卫 type Fish = { swim: () => void }; type Bird = { fly: () => void }; function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; } function move(pet: Fish | Bird) { if (isFish(pet)) { pet.swim(); // pet 被收窄为 Fish } else { pet.fly(); // pet 被收窄为 Bird } } // 字面量类型守卫 type Direction = "left" | "right" | "up" | "down"; function moveInDirection(direction: Direction) { switch (direction) { case "left": // direction: "left" break; case "right": // direction: "right" break; // ... 其他情况 } } 5.2 模板字面量类型
TypeScript 4.1 引入了模板字面量类型,可以进行字符串级别的类型操作。
// 基础模板字面量类型 type World = "world"; type Hello = `hello ${World}`; // "hello world" // 从属性名推断事件名 type EventName = "click" | "scroll"; type HandlerName = `on${Capitalize<EventName>}`; // "onClick" | "onScroll" // 路径类型生成 type Path<T, K extends keyof T = keyof T> = K extends string ? T[K] extends object ? `${K}` | `${K}.${Path<T[K]>}` : `${K}` : never; interface Config { server: { host: string; port: number; }; database: { url: string; timeout: number; }; } type ConfigPath = Path<Config>; // "server" | "server.host" | "server.port" | "database" | "database.url" | "database.timeout" // 使用模板字面量类型进行字符串验证 type Email = `${string}@${string}.${string}`; function sendEmail(email: Email) { // 确保 email 符合邮箱格式 console.log(`Sending to ${email}`); } sendEmail("user@example.com"); // OK // sendEmail("invalid-email"); // 错误 5.3 命名元组与可变元组
// 命名元组 function createPoint(x: number, y: number): [x: number, y: number] { return [x, y]; } const point = createPoint(1, 2); console.log(point.x); // 错误:元组没有 x 属性,但 IDE 会提示命名 // 可变元组类型 type Tuple = [number, string]; type Push<T extends any[], U> = [...T, U]; type Pop<T extends any[]> = T extends [...infer R, any] ? R : never; type Pushed = Push<Tuple, boolean>; // [number, string, boolean] type Popped = Pop<[1, "a", true]>; // [1, "a"] // 元组过滤 type FilterOut<T extends any[], U> = T extends [infer First, ...infer Rest] ? [First] extends [U] ? FilterOut<Rest, U> : [First, ...FilterOut<Rest, U>] : []; type Filtered = FilterOut<[1, "a", 2, "b", 3], string>; // [1, 2, 3] 5.4 递归类型与无限深度
// 深度只读 type DeepReadonly2<T> = T extends object ? T extends Function ? T : { readonly [P in keyof T]: DeepReadonly2<T[P]> } : T; // 深度可变 type DeepMutable<T> = T extends object ? T extends Function ? T : { -readonly [P in keyof T]: DeepMutable<T[P]> } : T; // 深度合并 type DeepMerge<A, B> = { [K in keyof A | keyof B]: K extends keyof B ? K extends keyof A ? DeepMerge<A[K], B[K]> : B[K] : K extends keyof A ? A[K] : never; }; // 深度部分更新 type UpdatePath<T, P extends string, V> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? { [OK in keyof T]: OK extends K ? UpdatePath<T[K], Rest, V> : T[OK] } & {} : T : P extends keyof T ? { [OK in keyof T]: OK extends P ? V : T[OK] } & {} : T; interface ConfigObject { a: { b: { c: number; d: string; }; e: boolean; }; f: string; } type Updated = UpdatePath<ConfigObject, "a.b.c", 42>; // { a: { b: { c: 42; d: string }; e: boolean }; f: string } 6. 性能考虑与最佳实践
虽然类型体操很强大,但过度使用可能会影响编译性能和代码可读性。
6.1 类型性能优化
// 避免过度复杂的递归类型 // 不好的例子:可能导致编译器栈溢出 type InfiniteRecursion<T> = { [K in keyof T]: InfiniteRecursion<T[K]> }; // 好的例子:限制递归深度 type SafeRecursion<T, Depth extends number = 0> = Depth extends 5 // 限制深度为5 ? T : T extends object ? { [K in keyof T]: SafeRecursion<T[K], [...Depth, any][number]> } : T; // 使用尾递归优化 type FlattenArray<T extends any[]> = T extends [infer First, ...infer Rest] ? First extends any[] ? [...FlattenArray<First>, ...FlattenArray<Rest>] : [First, ...FlattenArray<Rest>] : []; // 使用类型缓存 type Cache<T> = T extends Promise<infer U> ? U : T; type CachedPromise<T> = Cache<Promise<T>>; // T 6.2 可读性与维护性
// 使用辅助类型而不是内联所有类型 // 不好的例子:难以阅读 type ComplexType = { [K in keyof T as K extends string ? `on${Capitalize<K>}` : never]: (arg: T[K]) => void; }; // 好的例子:分解为多个辅助类型 type AddOnPrefix<K extends string> = `on${Capitalize<K>}`; type EventHandler<T> = { [K in keyof T as AddOnPrefix<K>]: (arg: T[K]) => void }; // 使用 JSDoc 注释复杂类型 /** * 创建一个类型,将对象的所有属性转换为事件处理函数 * @template T - 输入对象类型 * @example * type Input = { click: MouseEvent; scroll: ScrollEvent }; * type Output = EventHandler<Input>; // { onClick: (arg: MouseEvent) => void; onScroll: (arg: ScrollEvent) => void } */ type EventHandler<T> = { [K in keyof T as `on${Capitalize<string & K>}`]: (arg: T[K]) => void; }; // 避免 any,使用 unknown 和类型断言 function safeParse(json: string): unknown { try { return JSON.parse(json); } catch { return undefined; } } // 使用 unknown 并在需要时进行类型收窄 const result = safeParse('{"name": "Alice"}'); if (result && typeof result === "object" && "name" in result) { console.log(result.name); // 安全的访问 } 7. 实战案例:构建类型安全的 API 客户端
让我们通过一个完整的实战案例来整合所有知识,构建一个类型安全的 API 客户端。
// 1. 定义 API 端点和对应的请求/响应类型 interface ApiSpec { // GET 请求 "GET /users": { request: void; response: { id: number; name: string; email: string }[]; }; "GET /users/:id": { request: { id: number }; response: { id: number; name: string; email: string; age: number }; }; "GET /posts": { request: { page?: number; limit?: number }; response: { id: number; title: string; content: string }[]; }; // POST 请求 "POST /users": { request: { name: string; email: string; age: number }; response: { id: number; name: string; email: string; age: number }; }; "POST /posts": { request: { title: string; content: string }; response: { id: number; title: string; content: string }; }; // PUT 请求 "PUT /users/:id": { request: { id: number; name?: string; email?: string; age?: number }; response: { id: number; name: string; email: string; age: number }; }; // DELETE 请求 "DELETE /users/:id": { request: { id: number }; response: { success: boolean; message: string }; }; } // 2. 辅助类型:从端点字符串中提取 HTTP 方法和路径 type ParseEndpoint<T extends string> = T extends `${infer Method} ${infer Path}` ? { method: Method; path: Path } : never; // 3. 辅助类型:替换路径参数(如 :id) type ReplacePathParams<Path extends string, Params> = Path extends `${infer Start}/:${infer Param}/${infer Rest}` ? `${Start}/${string}/${Rest}` // 将 :param 替换为 string : Path extends `${infer Start}/:${infer Param}` ? `${Start}/${string}` : Path; // 4. 创建 API 客户端 class ApiClient { private baseUrl: string; private headers: Record<string, string>; constructor(baseUrl: string, headers: Record<string, string> = {}) { this.baseUrl = baseUrl; this.headers = headers; } // 核心请求方法 async request<T extends keyof ApiSpec>( endpoint: T, params: ApiSpec[T]["request"] ): Promise<ApiSpec[T]["response"]> { type Parsed = ParseEndpoint<T>; type Method = Parsed["method"]; type Path = Parsed["path"]; // 解析方法和路径 const [method, pathTemplate] = endpoint.split(" ") as [Method, Path]; // 处理路径参数 let path = pathTemplate as string; const pathParams: string[] = []; // 提取路径参数(如 :id) const paramRegex = /:([^/]+)/g; let match; while ((match = paramRegex.exec(path)) !== null) { const paramName = match[1]; if (params && (params as any)[paramName] !== undefined) { pathParams.push((params as any)[paramName]); path = path.replace(`:${paramName}`, (params as any)[paramName]); } } // 构建 URL const url = `${this.baseUrl}${path}`; // 准备请求配置 const config: RequestInit = { method, headers: { 'Content-Type': 'application/json', ...this.headers } }; // 处理请求体 if (method === 'POST' || method === 'PUT') { // 移除路径参数后的剩余参数作为请求体 const bodyParams = { ...params }; pathParams.forEach(param => { const key = Object.keys(bodyParams).find(k => (bodyParams as any)[k] === param); if (key) delete (bodyParams as any)[key]; }); if (Object.keys(bodyParams).length > 0) { config.body = JSON.stringify(bodyParams); } } // 发送请求 const response = await fetch(url, config); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json() as Promise<ApiSpec[T]["response"]>; } // 便捷方法 async get<T extends keyof ApiSpec>( endpoint: T & `GET ${string}`, params: ApiSpec[T]["request"] ): Promise<ApiSpec[T]["response"]> { return this.request(endpoint, params); } async post<T extends keyof ApiSpec>( endpoint: T & `POST ${string}`, params: ApiSpec[T]["request"] ): Promise<ApiSpec[T]["response"]> { return this.request(endpoint, params); } async put<T extends keyof ApiSpec>( endpoint: T & `PUT ${string}`, params: ApiSpec[T]["request"] ): Promise<ApiSpec[T]["response"]> { return this.request(endpoint, params); } async delete<T extends keyof ApiSpec>( endpoint: T & `DELETE ${string}`, params: ApiSpec[T]["request"] ): Promise<ApiSpec[T]["response"]> { return this.request(endpoint, params); } } // 5. 使用示例 async function main() { const client = new ApiClient("https://api.example.com"); // 完全类型安全的调用 try { // GET /users - 推断返回类型为 { id: number; name: string; email: string }[] const users = await client.get("GET /users", {}); console.log(users[0].name); // 类型安全 // GET /users/:id - 推断返回类型为 { id: number; name: string; email: string; age: number } const user = await client.get("GET /users/:id", { id: 1 }); console.log(user.age); // 类型安全 // POST /users - 推断请求和响应类型 const newUser = await client.post("POST /users", { name: "Alice", email: "alice@example.com", age: 30 }); console.log(newUser.id); // 类型安全 // GET /posts - 带查询参数 const posts = await client.get("GET /posts", { page: 1, limit: 10 }); console.log(posts.length); // 类型安全 // PUT /users/:id - 部分更新 const updatedUser = await client.put("PUT /users/:id", { id: 1, name: "Alice Updated" }); console.log(updatedUser.name); // 类型安全 // DELETE /users/:id const result = await client.delete("DELETE /users/:id", { id: 1 }); console.log(result.success); // 类型安全 } catch (error) { console.error("API error:", error); } } // 6. 高级用法:中间件和拦截器 class ApiClientWithMiddleware extends ApiClient { private middlewares: Array<(config: RequestInit) => RequestInit> = []; use(middleware: (config: RequestInit) => RequestInit): void { this.middlewares.push(middleware); } async request<T extends keyof ApiSpec>( endpoint: T, params: ApiSpec[T]["request"] ): Promise<ApiSpec[T]["response"]> { // 应用中间件 let config: RequestInit = { method: endpoint.split(" ")[0] }; for (const middleware of this.middlewares) { config = middleware(config); } // 调用父类方法 return super.request(endpoint, params); } } // 7. 错误处理和重试逻辑 class ResilientApiClient extends ApiClient { private maxRetries: number; private retryDelay: number; constructor(baseUrl: string, maxRetries: number = 3, retryDelay: number = 1000) { super(baseUrl); this.maxRetries = maxRetries; this.retryDelay = retryDelay; } async request<T extends keyof ApiSpec>( endpoint: T, params: ApiSpec[T]["request"], retryCount: number = 0 ): Promise<ApiSpec[T]["response"]> { try { return await super.request(endpoint, params); } catch (error) { if (retryCount < this.maxRetries) { console.log(`Retry ${retryCount + 1}/${this.maxRetries}...`); await new Promise(resolve => setTimeout(resolve, this.retryDelay)); return this.request(endpoint, params, retryCount + 1); } throw error; } } } // 8. 类型测试和验证 type AssertEqual<T, U> = (<V>() => V extends T ? 1 : 2) extends (<V>() => V extends U ? 1 : 2) ? true : false; // 测试类型推断是否正确 type Test1 = AssertEqual<ApiSpec["GET /users"]["response"], { id: number; name: string; email: string }[]>; type Test2 = AssertEqual<ApiSpec["POST /users"]["request"], { name: string; email: string; age: number }>; // 如果类型不匹配,TypeScript 会报错 const test1: Test1 = true; // 应该为 true const test2: Test2 = true; // 应该为 true export { ApiClient, ApiClientWithMiddleware, ResilientApiClient, main }; 8. 总结
TypeScript 的类型系统是一个强大而复杂的工具,从基础的类型注解到高级的类型体操,它为我们提供了在复杂业务场景中保持类型安全和代码质量的能力。关键要点包括:
- 基础是关键:扎实掌握基础类型、接口、联合类型等是进行高级类型操作的前提。
- 泛型是核心:泛型编程是解决复杂业务场景的核心工具,它提供了代码重用和类型安全的平衡。
- 类型推断是利器:充分利用 TypeScript 的类型推断能力,减少冗余的类型注解。
- 高级类型体操:在必要时使用映射类型、条件类型、递归类型等高级特性解决复杂问题。
- 性能与可读性:在使用高级类型时,始终考虑编译性能和代码可读性。
- 实战应用:通过实际案例(如 API 客户端、表单验证、状态管理)将理论知识转化为实践能力。
通过深入理解和熟练运用 TypeScript 的类型系统,开发者可以在复杂业务场景中编写出更加健壮、可维护和类型安全的代码。记住,类型系统的最终目标是帮助我们编写更好的代码,而不是为了展示类型体操技巧。在实际开发中,应该根据具体需求选择合适的类型复杂度,找到类型安全、开发效率和代码可读性之间的最佳平衡点。
支付宝扫一扫
微信扫一扫