
1. 泛型基础概念与核心价值泛型Generics是TypeScript中最重要的特性之一它允许我们创建可重用的组件这些组件可以支持多种类型而不是单一类型。这就像是一个万能容器你可以指定它将来要装什么类型的东西而不是在定义时就固定死。在实际开发中泛型主要解决两个核心问题类型安全避免运行时类型错误代码复用减少重复的类型定义举个生活中的例子泛型就像是一个可调节大小的收纳盒。你可以告诉它我现在要装书它就会自动调整成适合书本的大小下次你想装衣服时它又能变成适合衣物的尺寸。这种灵活性正是泛型最大的优势。2. 泛型语法深度解析2.1 基础语法结构泛型的基本语法使用尖括号来定义类型参数function identityT(arg: T): T { return arg; }这里的T就是类型变量它捕获用户传入的类型比如number、string等并在函数内部使用这个类型。2.2 多类型参数泛型可以支持多个类型参数function swapT, U(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]]; }这种多类型参数在处理元组、映射关系等场景特别有用。2.3 泛型约束有时我们需要限制泛型的类型范围这时可以使用extends关键字interface Lengthwise { length: number; } function loggingIdentityT extends Lengthwise(arg: T): T { console.log(arg.length); return arg; }这个约束确保传入的参数必须有length属性否则TypeScript会在编译时报错。3. 泛型在实战中的应用场景3.1 集合类数据结构泛型最常见的应用场景就是各种集合类class QueueT { private data: T[] []; push(item: T) { this.data.push(item); } pop(): T | undefined { return this.data.shift(); } } const numberQueue new Queuenumber(); numberQueue.push(1); numberQueue.push(2); // 错误类型string不能赋给类型number3.2 API响应处理在处理API响应时泛型可以确保我们得到正确的类型interface ApiResponseT { code: number; message: string; data: T; } function fetchUser(): PromiseApiResponseUser { return axios.get(/api/user); }3.3 高阶函数泛型在高阶函数中也非常有用function mapT, U(array: T[], fn: (item: T) U): U[] { return array.map(fn); } const numbers [1, 2, 3]; const strings map(numbers, (n) n.toString());4. 高级泛型技巧4.1 条件类型TypeScript 2.8引入了条件类型可以基于类型关系进行选择type IsStringT T extends string ? true : false; type A IsStringstring; // true type B IsStringnumber; // false4.2 映射类型映射类型允许你基于旧类型创建新类型type ReadonlyT { readonly [P in keyof T]: T[P]; }; interface Person { name: string; age: number; } type ReadonlyPerson ReadonlyPerson;4.3 类型推断使用infer关键字可以在条件类型中进行类型推断type ReturnTypeT T extends (...args: any[]) infer R ? R : never; function foo() { return 123; } type FooReturn ReturnTypetypeof foo; // number5. 常见问题与解决方案5.1 泛型擦除问题TypeScript的泛型只在编译时存在运行时会被擦除。这意味着function genericTypeCheckT(value: T) { // 错误运行时T不存在 if (value instanceof T) { // ... } }解决方案是使用类型谓词或工厂模式。5.2 默认类型参数可以为泛型参数指定默认类型interface PaginationT any { data: T[]; total: number; }这在某些情况下可以简化类型声明。5.3 泛型与重载当需要处理多种可能的参数类型组合时可以考虑使用函数重载function process(value: string): string; function process(value: number): number; function processT(value: T): T { // 实现逻辑 return value; }6. 性能考量与最佳实践6.1 避免过度泛型化虽然泛型很强大但过度使用会导致代码难以理解和维护。一个好的经验法则是只有当代码会被多种类型使用时才考虑泛型。6.2 类型推断优先TypeScript的类型推断非常强大很多时候不需要显式指定泛型类型// 不需要显式指定string const names [Alice, Bob]; // string[]类型6.3 文档注释为复杂的泛型类型添加文档注释/** * 将对象的所有属性转为可选 * template T - 原始对象类型 */ type PartialT { [P in keyof T]?: T[P]; };7. TypeScript 7.0变更与泛型根据最新网络热词TypeScript 7.0将弃用一些配置选项// tsconfig.json { compilerOptions: { // 已弃用将在7.0中移除 baseUrl: ./, moduleResolution: node10 } }这些变更可能会影响泛型类型解析的路径解析方式建议开发者提前迁移到现代模块解析策略。8. 面试常见泛型问题8.1 实现Pick工具类型type MyPickT, K extends keyof T { [P in K]: T[P]; }; interface Todo { title: string; description: string; completed: boolean; } type TodoPreview MyPickTodo, title | completed;8.2 实现Partial工具类型type MyPartialT { [P in keyof T]?: T[P]; };8.3 泛型约束与条件类型结合type NonNullablePropertyKeysT { [K in keyof T]: T[K] extends null | undefined ? never : K }[keyof T]; type NonNullablePropertiesT PickT, NonNullablePropertyKeysT;9. 泛型与其他语言的对比9.1 与Java泛型的区别TypeScript泛型编译时类型检查运行时类型擦除支持更复杂的类型操作如条件类型、映射类型Java泛型同样有类型擦除不支持TypeScript那样的高级类型操作有通配符概念? extends, ? super9.2 与C#泛型的区别C#泛型运行时保留类型信息支持泛型约束where T : class不支持TypeScript那样的类型操作10. 实战构建一个类型安全的EventEmitter让我们用泛型实现一个类型安全的事件发射器type EventMap Recordstring, any; interface EventEmitterT extends EventMap { onK extends keyof T(event: K, listener: (payload: T[K]) void): void; emitK extends keyof T(event: K, payload: T[K]): void; } class MyEmitter implements EventEmitter{ click: { x: number; y: number }; hover: { element: HTMLElement }; } { // 实现细节... } const emitter new MyEmitter(); emitter.on(click, ({ x, y }) console.log(x, y)); emitter.emit(click, { x: 10, y: 20 });这个实现确保了事件名称和载荷类型的严格对应完全类型安全。