TypeScript 泛型
TypeScript 泛型的学习建议:学习泛型,建议分两步:首先掌握泛型的基本概念,然后再掌握泛型约束的使用。
1、泛型是什么?
泛型是一种特殊的变量,只用于表示类型而不是值,称之为:类型变量。泛型通常用尖括号<T>
来表示,T 是泛型的常用名称,T 代表Type
。实际上 T 可以用任何有效名称代替,除了 T 之外,以下是常见泛型变量代表的意思:
- K(Key):表示对象中的键类型;
- V(Value):表示对象中的值类型;
- E(Element):表示元素类型。
2、为什么使用泛型?
为更好地理解使用泛型的理由,可以看下面的 getArray 函数,它可以生成 any 类型的项的数组:
function getArray(items : any[]) : any[] {
return new Array().concat(items);
}
我们可以调用 getArray 函数并向其传递一个数字数组来声明 numberArray 变量,也可以使用一个字符串数组来声明 stringArray 变量。 但是,由于使用了 any 类型,因此没有什么可以阻止代码将 string 赋值到 numberArray 或将 number 赋值到 stringArray。
let numberArray = getArray([5, 10, 15, 20]);
let stringArray = getArray(['Cats', 'Dogs', 'Birds']);
numberArray.push(25); // OK
stringArray.push('Rabbits'); // OK
numberArray.push('This is not a number'); // OK
stringArray.push(30); // OK
console.log(numberArray); // [5, 10, 15, 20, 25, "This is not a number"]
console.log(stringArray); // ["Cats", "Dogs", "Birds", "Rabbits", 30]
如果想要在调用函数时确定数组将包含的值的类型,然后让 TypeScript 对传递给它的值进行类型检查以确保它们属于该类型,该怎么操作? 这时泛型就可以发挥作用了。下面的例子使用泛型重写 getArray 函数。现在,它可以接受你在调用函数时指定的任何类型。
function getArray<T>(items : T[]) : T[] {
return new Array<T>().concat(items);
}
泛型定义一个或多个“类型变量”来标识要传递给组件的一个或多个类型,用尖括号 (< >) 括起来。在上面的示例中,函数中的类型变量称为 <T>。
指定类型变量后,可以使用它来代替参数中的类型、返回类型或将其置于函数中要添加类型批注的任何其他位置。类型变量 T 可用于任何需要类型批注的位置。 在 getArray 函数中,它用于指定 items 参数的类型、函数返回类型和返回新的项数组。
若要调用函数并向其传递类型,请将 <type> 追加到函数名称。例如,getArray<number> 指示函数仅接受 number 值的数组,并返回 number 值的数组。 因为类型已指定为 number,所以 TypeScript 会预期将 number 值传递给函数,如果传递的是其他值,则会引发错误。
在下面例子中,通过更新 numberArray 和 stringArray 的变量声明以调用具有所需类型的函数,TypeScript 可阻止将无效项添加到数组中。
let numberArray = getArray<number>([5, 10, 15, 20]);
numberArray.push(25); // OK
numberArray.push('This is not a number'); // Generates a compile time type check error
let stringArray = getArray<string>(['Cats', 'Dogs', 'Birds']);
stringArray.push('Rabbits'); // OK
stringArray.push(30); // Generates a compile time type check error
3、什么是泛型约束?为什么使用泛型约束?如何使用泛型约束?
泛型约束即是对泛型的类型进行约束控制,当在函数里使用泛型参数的属性或者方法时,就需要对泛型进行约束。如下所示的 identity 函数可以接受你选择要传递到类型变量的任何类型。但是,在这种情况下,你应该将 value 参数可以接受的类型限制为可以对其执行添加操作的一系列类型,而不是接受任何可能的类型。这种情况称为“泛型约束”。
为了实现泛型约束,我们自定义 type 类型,然后使用自定义的 type 类型 extend 类型变量 <T>。下面的示例将 ValidTypes 声明为带有 string 和 number 的元组。 然后,用它来扩展类型变量 <T>。 现在,只能将 number 或 string 类型传递给类型变量。
type ValidTypes = string | number;
function identity<T extends ValidTypes, U> (value: T, message: U) : T {
let result: T = value + value; // Error
console.log(message);
return result
}
let returnNumber = identity<number, string>(100, 'Hello!'); // OK
let returnString = identity<string, string>('100', 'Hola!'); // OK
let returnBoolean = identity<boolean, string>(true, 'Bonjour!'); // Error: Type 'boolean' does not satisfy the constraint 'ValidTypes'.
另外,在泛型约束中,我们还可以将类型限制为另一个对象的属性。下面的例子将 extends
与 keyof
运算符一起使用,该运算符采用对象类型并生成其键的字符串或数字文本并集。此处,K extends keyof T
确保键参数的类型对于分配给 pet
的类型来说是正确的。
function getPets<T, K extends keyof T>(pet: T, key: K) {
return pet[key];
}
let pets1 = { cats: 4, dogs: 3, parrots: 1, fish: 6 };
let pets2 = { 1: "cats", 2: "dogs", 3: "parrots", 4: "fish"}
console.log(getPets(pets1, "fish")); // Returns 6
console.log(getPets(pets2, "3")); // Error