# Generics 泛型
假如:需要一个函数,返回传入它的内容。不使用泛型时,它会是这样的:
// 案例:
// 这个函数接受两个参数。第一个参数为任意类型的值,第二个参数为数值类型的值,默认为 5。
const getArray = (value: any, times: number = 5): any[] => {
return new Array(times).fill(value);
};
getArray([1], 2).forEach(item => {
console.log(item.length);
});
getArray(2, 3).forEach(item => {
console.log(item.length); // 报错
});
我们调用了两次这个方法,使用 forEach 方法遍历得到的数组,在传入 forEach 的函数中获取当前遍历到的数组元素的 length 属性。
上面例子中第二次调用getArray
的返回值每个元素应该是数值类型,遍历这个数组时我们获取数值类型的length属性也没报错,因为这里item的类型是any。
上面的示例,定时any时,丢失了一些信息:
传入的类型与返回的类型应该是相同的;
如果我们传入一个数字,我们只知道任何类型的值都有可能被返回;
于是此时需要一种方法使返回值的类型与传入参数的类型是相同的。
# 定义
泛型指的是,在定义函数、接口或类的时候不预先指定数据类型,而在使用时再指定类型的特性。
作用:泛型可以提升应用的可重用性,如使用其创建组件,则可以使组件可以支持多种数据类型。
拿上面这个例子中的逻辑来举例:
const getArray = <T>(value: T, times: number = 5): T[] => {
return new Array(times).fill(value);
};
这个 T 在这次函数定义中就代表某一种类型,它可以是基础类型,也可以是联合类型等高级类型。
现在我们再来调用一下这个 getArray 函数:
getArray<number[]>([1, 2], 3).forEach(item => {
console.log(item.length);
});
getArray<number>(2, 3).forEach(item => {
console.log(item.length); // 类型“number”上不存在属性“length”
});
在调用 getArray 的时候,在方法名后面使用<>
传入了我们的泛型变量 T 的类型number[]
,那么在定义 getArray 函数时使用 T 指定类型的地方,都会使用number[]
指定。
# 泛型变量
泛型变量,这是一种特殊的变量,只用于表示类型而不是值。
但是,你也可以省略这个<number[]>
,TypeScript 会根据你传入函数的 value 值的类型进行推断:
getArray(2, 3).forEach(item => {
console.log(item.length); // 类型“number”上不存在属性“length”
});
由于我们在定义getArray时,给value也定义了T类型,那么,传入实参时,会同时将实参的类型传递给类型变量 T。
应用场景:交换两个数组元素
不使用泛型会丢失数据类型:
function swap(tuple) {
return [tuple[1], tuple[0]]
}
使用泛型后,不仅会保有类型推断,还可以直接调用实例的方法:
function swapGeneric<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]]
}
const result2 = swapGeneric(['string', 0.123])
// ts的类型推断系统能够明确得知第一个元素会是数值,而第二个元素会是字符串
result2[0].toFixed(2)
result2[1].toLocaleUpperCase()