泛型函数
对于一个有输入和输出的函数, 如:
// 返回一个数组的第一个元素
function firstElement(arr: any[]) {
return arr[0];
}
在这里,我们不知道 arr 的类型,所以使用 any,最终返回的也是一个 any。这样就会丢失类型检查的能力。
我们更希望返回数组元素的类型
在 Typescript 中,当我们想描述两个值之间关系时,使用泛型。
在函数签名中声明一个类型参数
function firstElement<Type>(arr: Type[]): Type | undefined {
return arr[0];
}
通过增加类型参数 Type, 我们可以捕获用户传入的类型,然后使用这个类型,输出这个类型。
// s is of type 'string'
const s = firstElement(["a", "b", "c"]);
// n is of type 'number'
const n = firstElement([1, 2, 3]);
// u is of type undefined
const u = firstElement([]);
这里不必指定 Type 的具体类型,因为具体的 type 会在执行过程中被推理出来。
我们也可以使用多个 type 参数。例如:
function map<Input, Output>(
arr: Input[],
func: (arg: Input) => Output
): Output[] {
return arr.map(func);
}
// Parameter 'n' is of type 'string'
// 'parsed' is of type 'number[]'
const parsed = map(["1", "2", "3"], (n) => parseInt(n));
约束参数类型
一般通过 extends 关键字来约束泛型的类型。
求两个参数中最长的那个
这要求 longest 的 2 个参数都必须要有 length 属性,且为 number 类型。这里的 2 个入参,可以为数组,也可以为字符串,或者其他有 length 属性的类型。所以这里的入参,我们用泛型来表示。但是,因为我们要求入参必须有 length 属性,所以我们需要对泛型进行约束。通过 extends 关键字,我们要求泛型必须有 length 属性。
function longest<Type extends { length: number }>(a: Type, b: Type) {
if (a.length >= b.length) {
return a;
} else {
return b;
}
}
// longerArray is of type 'number[]'
const longerArray = longest([1, 2], [1, 2, 3]);
// longerString is of type 'alice' | 'bob'
const longerString = longest("alice", "bob");
// Error! Numbers don't have a 'length' property
const notOK = longest(10, 100);
泛型,就是将 2 个或多个值与相同类型相关联。
在泛型的调用中指定参数类型
function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
return arr1.concat(arr2);
}
通常,我们在调用时,typescript 会自动推断出类型,这里与定义的泛型不一致,所以会报错。
const arr = combine([1, 2, 3], ["hello"]);
// Type 'string' is not assignable to type 'number'.
这时,我们可以指定泛型的类型
const arr = combine<number | string>([1, 2, 3], ["hello"]);
编写好的泛型
-
- 如果可能,使用类型参数本身而不是对其进行约束
-
- 始终使用尽可能少的类型参数
-
- 如果一个类型参数只出现在一个位置,强烈重新考虑你是否真的需要它(即类型参数在函数签名中至少有效的使用 2 次)
-
- 为回调编写函数类型时,切勿编写可选参数,除非您打算在不传递该参数的情况下调用该函数
function myForEach(arr: any[], callback: (arg: any, index?: number) => void) { for (let i = 0; i < arr.length; i++) { callback(arr[i], i); } } myForEach([1, 2, 3], (a, i) => { console.log(i.toFixed()); }); // 'i' is possibly 'undefined'.
-
- 实现的签名从外部是不可见的。编写重载函数时,您应该始终在函数的实现之上有两个或更多签名。
function fn(x: string): void; function fn() { // 这里是函数的实现部分,此处定义的没有参数的签名,是不可见的 // ... } // Expected to be able to call with zero arguments fn();