TypeScript 泛型
泛型是TypeScript最强大的特性之一,它可以让我们在编写代码时保持类型的灵活性,同时又不失类型安全。
1. 泛型基础
1.1 泛型函数
// 基本泛型函数
function identity<T>(value: T): T {
return value;
}
// 使用方式
let result1 = identity<string>("hello"); // 显式指定类型
let result2 = identity(42); // 类型推断
// 多个类型参数
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
let pair1 = pair<string, number>("hello", 42);
let pair2 = pair(true, [1, 2, 3]);
1.2 泛型接口
// 泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
// 实现泛型接口
let myIdentity: GenericIdentityFn<number> = function(arg: number): number {
return arg;
};
// 泛型接口作为类型
interface Collection<T> {
add(item: T): void;
remove(item: T): void;
getItems(): T[];
}
class List<T> implements Collection<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
remove(item: T): void {
const index = this.items.indexOf(item);
if (index > -1) {
this.items.splice(index, 1);
}
}
getItems(): T[] {
return [...this.items];
}
}
1.3 泛型类
// 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
constructor(zero: T, addFn: (x: T, y: T) => T) {
this.zeroValue = zero;
this.add = addFn;
}
}
// 数字类型实例
let numericAdder = new GenericNumber<number>(0, (x, y) => x + y);
console.log(numericAdder.add(10, 20)); // 30
// 字符串类型实例
let stringAdder = new GenericNumber<string>("", (x, y) => x + y);
console.log(stringAdder.add("Hello ", "World")); // "Hello World"
2. 泛型约束
2.1 使用extends关键字
// 基本约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在我们知道arg具有length属性
return arg;
}
// 可以传入string或array
loggingIdentity("hello"); // OK
loggingIdentity([1, 2, 3]); // OK
loggingIdentity(3); // Error: number没有length属性
// 多重约束
interface HasName {
name: string;
}
interface HasAge {
age: number;
}
function processUser<T extends HasName & HasAge>(user: T): void {
console.log(`${user.name} is ${user.age} years old`);
}
2.2 keyof约束
// 使用keyof获取对象的键类型
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let person = {
name: "John",
age: 30,
location: "New York"
};
// 类型安全的属性访问
let name = getProperty(person, "name"); // OK
let age = getProperty(person, "age"); // OK
let email = getProperty(person, "email"); // Error: "email"不是person的属性
3. 高级泛型模式
3.1 条件类型
// 基本条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// 分布式条件类型
type NonNullable<T> = T extends null | undefined ? never : T;
type C = NonNullable<string | number | null>; // string | number
// 推断类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
function fn(x: number): string {
return x.toString();
}
type FnReturn = ReturnType<typeof fn>; // string
3.2 映射类型
// 基本映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
// 实际应用
interface Todo {
title: string;
description: string;
completed: boolean;
}
type ReadonlyTodo = Readonly<Todo>;
type PartialTodo = Partial<Todo>;
// 条件映射类型
type NonNullableProperties<T> = {
[P in keyof T]: NonNullable<T[P]>;
};
4. 最佳实践
4.1 泛型命名约定
// 常用泛型参数名称
T - Type(通用类型参数)
K - Key(对象键类型)
V - Value(对象值类型)
E - Element(元素类型)
P - Property(属性类型)
R - Return(返回值类型)
S,U - 第二、第三个类型参数
// 示例
function map<T, U>(array: T[], fn: (item: T) => U): U[] {
return array.map(fn);
}
4.2 泛型工具类型
// 常用工具类型
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type Record<K extends keyof any, T> = {
[P in K]: T;
};
type Exclude<T, U> = T extends U ? never : T;
// 实际应用
interface User {
id: number;
name: string;
email: string;
password: string;
}
type PublicUser = Pick<User, "id" | "name" | "email">;
type UserRoles = Record<"admin" | "user" | "guest", User>;
4.3 泛型约束最佳实践
// 使用类型参数默认值
interface DefaultGeneric<T = string> {
value: T;
}
// 组合使用泛型约束
interface HasId {
id: number;
}
interface HasName {
name: string;
}
function processEntity<T extends HasId & HasName>(entity: T): void {
console.log(`Processing ${entity.name} with ID ${entity.id}`);
}
// 泛型工厂
interface GenericFactory<T> {
create(): T;
update(item: Partial<T>): T;
delete(id: number): void;
}
5. 常见问题和解决方案
// 问题1:泛型类型推断失败
function createPair<T>(first: T): [T, T] {
// 错误示范
return [first, null]; // Error: null不能赋值给T
// 正确做法
return [first, first];
}
// 问题2:泛型约束过于宽松
function processItems<T>(items: T[]): void {
// 错误示范
items.forEach(item => console.log(item.toString())); // Error: T可能没有toString方法
// 正确做法
function processItems<T extends { toString(): string }>(items: T[]): void {
items.forEach(item => console.log(item.toString()));
}
}
// 问题3:泛型类型丢失
class DataContainer<T> {
private data: T;
// 错误示范
setData(value: any) { // 丢失了类型安全
this.data = value;
}
// 正确做法
setData(value: T) {
this.data = value;
}
}