泛型 (Generics)
泛型是 TypeScript 中一个非常强大的特性,它允许你编写更加灵活和可重用的代码。通过泛型,你可以定义函数、接口或类时指定一个或多个类型参数,这些参数可以在使用时被具体化为实际的类型。这不仅提高了代码的复用性,还增强了类型安全性。
基本概念
- 类型参数:在定义泛型时使用的占位符,用于表示将来会被具体化的类型。
- 类型推断:当调用泛型函数或实例化泛型类时,TypeScript 可以根据传入的参数自动推断出具体的类型。
- 约束:可以对类型参数进行约束,确保它们符合特定的类型结构。
示例解析
示例 1: 简单的泛型函数
typescript
function identity<T>(arg: T): T {
return arg;
}
console.log(identity<string>("hello")); // 输出: "hello"
console.log(identity<number>(42)); // 输出: 42在这个例子中,identity 函数接受一个类型参数 T,它可以是任何类型。返回值的类型也是 T,即与输入参数的类型相同。
示例 2: 使用泛型约束
有时你可能希望限制泛型参数的类型范围,确保它们至少具有某些属性或方法。这时可以使用泛型约束。
typescript
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在我们知道 arg 有 length 属性
return arg;
}
loggingIdentity({ length: 10, value: "hello" });在这个例子中,T 被约束为必须实现 Lengthwise 接口,因此我们可以安全地访问 arg.length 属性。
示例 3: 多个类型参数
你可以定义多个类型参数来处理更复杂的场景。
typescript
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
console.log(swap([1, "hello"])); // 输出: ["hello", 1]示例 4: 默认类型参数
TypeScript 允许你为泛型参数提供默认类型,这样如果调用时不指定类型参数,将使用默认类型。
typescript
function makeArray<T = string>(...args: T[]): T[] {
return args;
}
console.log(makeArray("a", "b", "c")); // 使用默认类型 string
console.log(makeArray<number>(1, 2, 3)); // 指定类型 number示例 5: 泛型接口
你还可以定义泛型接口,使接口本身或其成员可以接受类型参数。
typescript
interface Box<T> {
contents: T;
}
const box: Box<string> = { contents: "hello" };示例 6: 泛型类
泛型不仅限于函数,你还可以创建泛型类,使类的属性和方法能够处理不同的类型。
typescript
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
const myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
return x + y;
};
console.log(myGenericNumber.add(10, 20)); // 输出: 30类型推断
TypeScript 在大多数情况下可以自动推断出泛型参数的具体类型,而不需要显式指定。
typescript
function identity<T>(arg: T): T {
return arg;
}
// 不需要显式指定 <string>
let output = identity("hello"); // 输出: "hello"泛型工具类型
TypeScript 提供了一些内置的泛型工具类型,可以帮助简化常见的类型操作:
Partial<T>:将T中的所有属性变为可选。Readonly<T>:将T中的所有属性变为只读。Record<K, T>:构建一个新的对象类型,其中键来自K,值来自T。Pick<T, K>:从T中挑选出由K指定的属性。Exclude<T, U>:从T中排除所有可以赋值给U的类型。Extract<T, U>:从T中提取出所有可以赋值给U的类型。
应用场景
- 复用代码:通过泛型,你可以编写适用于多种类型的代码,而不需要为每种类型单独编写实现。
- 类型安全增强:泛型确保了代码在编译时就能捕获类型错误,提高了程序的健壮性。
- API 设计:使用泛型可以使 API 更加灵活和强大,让用户可以根据需要选择合适的方法调用。
总结
泛型是 TypeScript 中一个非常重要的特性,它使得代码更加灵活、可复用且类型安全。通过合理使用泛型,你可以编写出更加通用和强大的代码。