一、泛型函数
TypeScript
泛型是一种可以使代码具有更高的可重用性和泛化能力的特性。通过泛型,我们可以定义一种通用的类型或函数,使其能够应对多种类型的输入。泛型在类、函数、接口等多种场景下都可以使用。
具体来说,在定义泛型函数时,我们可以使用来表示一个类型变量,这样我们就可以在函数中使用这个泛型类型来作为参数类型、返回值类型或变量类型等。例如:
function echo<T>(arg: T): T {
return arg;
}
let myIdentity: <T>(arg: T) => T = echo;
在这个例子中,我们定义了一个echo函数,它使用泛型类型变量T作为输入参数类型和返回值类型,这样我们就可以使用不同类型的参数来调用该函数,例如:
console.log(echo('Hello TypeScript!')); // 输出:Hello TypeScript!
console.log(echo(123)); // 输出:123
二、泛型类
在类的定义中使用泛型,也可以大大提高代码的复用性和灵活性。例如下面的代码:
class Stack<T> {
private items: T[] = [];
push(item: T) {
this.items.push(item);
}
pop(): T {
return this.items.pop();
}
}
let strStack = new Stack<string>();
strStack.push('apple');
strStack.push('banana');
strStack.push('cherry');
console.log(strStack.pop());
console.log(strStack.pop());
console.log(strStack.pop());
在上面的代码中,定义了一个Stack类,并声明了泛型类型T,用于在push和pop方法中进行类型的转换。通过传入不同类型的参数,可以得到不同类型的Stack实例。
三、泛型接口
interface Map<K, V> {
set(key: K, val: V): void;
get(key: K): V | undefined;
has(key: K): boolean;
clear(): void;
}
let numMap: Map<number, string> = {
set(key: number, val: string) {
// 添加键值对
},
get(key: number): string | undefined {
// 获取键值对
},
has(key: number): boolean {
// 判断是否存在
},
clear() {
// 清空Map
}
};
numMap.set(1, 'apple');
numMap.set(2, 'banana');
console.log(numMap.get(1));
console.log(numMap.get(2));
在上面的代码中,定义了一个Map接口,并使用泛型K和V来约束键和值的类型。通过传入不同类型的参数,可以得到不同类型的Map实例,并实现对不同类型键值对的操作。
四、泛型约束、泛型限制用法<T> extends {属性:类型}
在 TypeScript 中,泛型约束是一种让泛型类型参数只能取某些特定类型的值的机制。这个特定类型的限制可以是一个接口、类、枚举或其他类型。
例如,假设我们有一个泛型函数,它接受一个类型参数 T 和一个对象参数 obj,这个函数返回 obj 中的第一个 T 类型的属性值:
function getPropertyValue<T>(obj: object): T {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
let value = obj[key];
if (value instanceof T) {
return value;
}
}
}
throw new Error(`No property of type ${T.name} found.`);
}
但是,这个函数可能会遇到一些问题。例如,如果我们调用 getPropertyValue({ a: ‘1’, b: 2, c: 3 }),那么会抛出一个错误,因为字符串 ‘1’ 不是一个 number 类型。
为了解决这个问题,我们可以使用泛型约束来限制 T 只能取某些特定类型的值。例如,我们可以要求 T 必须是实现了 Number 接口的类型:
interface Number {
new (value?: any): number;
(value?: any): number;
readonly NaN: number;
readonly MAX_VALUE: number;
readonly MIN_VALUE: number;
readonly NEGATIVE_INFINITY: number;
readonly POSITIVE_INFINITY: number;
}
function getPropertyValue<T extends Number>(obj: object): T {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
let value = obj[key];
if (value instanceof T) {
return value;
}
}
}
throw new Error(`No property of type ${T.name} found.`);
}
现在,如果我们调用 getPropertyValue({ a: ‘1’, b: 2, c: 3 }),就会得到一个类型错误,因为 ‘1’ 不是一个实现了 Number 接口的类型。但是,如果我们调用 getPropertyValue({ a: 1, b: 2, c: 3 }),就能得到正确的结果了。
另一泛型约束\<key extends keyof Type>
在 TypeScript 中,<key extends keyof Type>
是一种泛型约束方式,用于限制一个泛型类型参数 key
的范围。
其中,keyof
关键字可以用于获取一个类型 Type
的所有属性名,返回一个字符串字面量类型,例如:
interface Person {
name: string;
age: number;
}
type PersonKeys = keyof Person; // "name" | "age"
在 <key extends keyof Type>
中,extends
关键字表示限制 key
的取值只能是 Type
类型中已有的属性名。也就是说,只有 key
取值为 Type
类型中已有的属性名才符合类型约束,例如:
function getValueByKey<T extends object, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = {
name: 'Bob',
age: 25,
};
const name = getValueByKey(person, 'name'); // name 的类型是 string
const age = getValueByKey(person, 'age'); // age 的类型是 number
const gender = getValueByKey(person, 'gender'); // 编译报错,gender 不是 person 中的属性
在上面的例子中,getValueByKey
函数接收两个参数:一个泛型类型参数 T
,代表输入对象的类型;一个泛型类型参数 K
,代表属性名的类型。
extends keyof T
约束了类型参数 K
必须是输入对象类型 T
中已存在的属性名。因此,调用 getValueByKey
函数传入一个不存在的属性名 gender
会引发编译错误。
五、使用泛型注意点
在编写泛型方法时,应该注意以下几点:
- 泛型形参的名称应该描述清楚其作用和范围,尽量不要使用单个字母的形参名称;
- 在使用泛型类型的属性或方法时,应该确保该属性或方法在所有可能的泛型类型中都是存在的,不要使用不存在的属性或方法;
- 在使用泛型类型的操作符时,应该考虑不同类型之间可能的差异性和兼容性问题,避免出现类型错误