TypeScript 的泛型是一个非常强大的特性,允许在定义函数、接口、类或类型别名时提供一种方法来确保类型安全的同时增加代码的灵活性和重用性。这里,我将通过一系列具体示例来详细解释如何在不同场景中使用 TypeScript 的泛型。
泛型基础
泛型函数
泛型函数可以适用于多种数据类型。这意味着函数可以接收不同类型的参数,返回相同类型的输出,而无需为每种类型编写多个函数定义。
function identity<T>(arg: T): T {
return arg;
}
let outputString = identity<string>("Hello World");
let outputNumber = identity<number>(100);
这里,identity
函数用一个类型变量 T
来捕获用户提供的类型(如 string
或 number
),然后再用这个类型来定义输入和输出的类型。
泛型接口
泛型接口可以用来定义包含多种类型的接口,这在设计大型系统或库时非常有用。
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
泛型类
泛型类使用泛型来工作于类的属性和方法。注意,泛型类不能用于静态成员。
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => x + y;
let myGenericString = new GenericNumber<string>();
myGenericString.zeroValue = "";
myGenericString.add = (x, y) => x + y;
泛型约束
你可能会想让你的泛型函数只接受某些类型。这是通过所谓的“泛型约束”来实现的。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property
return arg;
}
loggingIdentity({ length: 10, value: 3 }); // OK
在这个示例中,loggingIdentity
现在需要一个至少有一个 length
属性的类型。
使用多个类型变量
TypeScript 泛型也支持多个类型变量,允许你在一个泛型中同时处理多种类型。
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
let result = merge({ name: "John" }, { age: 30 });
console.log(result.name); // John
console.log(result.age); // 30
这里,merge
函数创建了两个对象的合并,返回一个同时包含两个对象属性的新对象。
默认泛型类型
你还可以为泛型类型参数指定默认类型,以便在不指定类型参数时使用。
interface Dictionary<T = string> {
[key: string]: T;
}
let numDict: Dictionary<number> = { val1: 10, val2: 20 };
let strDict: Dictionary = { msg1: "Hello", msg2: "World" };
在这个例子中,如果不指定 Dictionary
泛型的类型,默认为 string
。
总结
泛型是 TypeScript 提供的一个非常强大的工具,它帮助开发者编写可复用且类型安全的代码。通过使用泛型,你可以写出灵活且易于维护的代码,这对于任何规模的项目都是非常有价值的。