"泛"就是广泛的意思,"型"就是数据类型。顾名思义,泛型就是适用于多种数据类型的一种类型。
它能够帮助我们构建出复用性更强的代码。
假如有如下函数:
function sum(num: number): number {
return num
}
此时函数的参数和返回值都必须是number类型的。
但是其它类型的数据,比如string、boolean或者自定义的Teacher等类型就没有办法使用这个函数。
虽然any可以解决这个问题,但是函数参数和返回值定义为any的时候,具体的类型信息就已经丢失了。
比如传入的是一个number,那么我们希望返回的并不是any类型,而是number类型。
所以,我们需要在函数中可以捕获到参数的类型是number,并且同时使用它来作为返回值的类型。
因此需要在这里使用一种特性的变量 - 类型变量(type variable),它作用于类型,而不是值!
在定义一个函数时,不决定这些参数的类型,而是让调用者以参数的形式告知函数参数应该是什么类型。
function sum<T>(num: T): T {
return num
}
我们可以把Type看做额外的一个参数,把类型参数化。
它可以做到, 在定义这个函数时, 不决定这些参数的类型, 而是让调用者以参数的形式告知, 这里的函数参数应该是什么类型。
函数定义时,<>的位置就是之后泛型的传入位置,比如上面函数的。
传入泛型时,只要把具体的泛型传递给即可。
这样函数定义时的,函数参数,和函数返回值的T类型,都会变成传入的具体参数类型。
sum<number>(100) // 声明sum函数的泛型为number类型,并传递number类型的参数
sum<{name: string}>({ name: "zs" }) // 声明sum函数的泛型为{name: string}类型
sum<number[]>([15, 25]) // 声明sum函数的泛型为number类型的数组
在这里会推导出它们是 字面量类型的,因为字面量类型对于我们的函数也是适用的
sum(100)
sum("test")
开发中可能会看到一些常用的名称,不同的名称会有不同语义化意义。
T:Type的缩写,类型
K、V:key和value的缩写,键值对
E:Element的缩写,元素
O:Object的缩写,对象
function fun<T, E>(arg1: T, arg2: E) {
console.log(arg1, arg2)
}
fun<number, string>(100, "test")
当一个函数有多个泛型时,可以用不同的泛型名称来标识。
上面的fun函数的参数一的类型由泛型T决定,而参数二由泛型E决定。
在定义接口的时候也可以使用泛型来对接口内的成员进行类型约束。
interface IInfo<T1, T2> {
name: T1
age: T2
}
const info: IInfo<string, number> = {
name: "zs",
age: 25
}
泛型接口是没有类型推导的, 但是可以有泛型默认值。
// 泛型接口定义默认类型
interface IPerson<T1 = string, T2 = number> {
name: T1
age: T2
}
const p: IPerson = {
name: "chenyq",
age: 123
}
class Point<T> {
x: T
y: T
z: T
constructor(x: T, y: T, z: T) {
this.x = x
this.y = y
this.z = y
}
}
// 泛型类自动推导类型
const point1 = new Point("1.33.2", "2.22.3", "4.22.1")
// 泛型类明确泛型类型
const point2 = new Point<string>("1.33.2", "2.22.3", "4.22.1")
const point3: Point<string> = new Point("1.33.2", "2.22.3", "4.22.1")
数组泛型的写法:
// 定义字符串数组
const arr1: string[] = ["a", "b", "c"]
const arr2: Array<string> = ["a", "b", "c"]
有时候希望传入的类型具有某些共性,但是这些共性可能不是在同一种类型中。
比如string和array都是有length的,或者某些对象也是会有length属性的。
那么只要是拥有length的属性都可以作为我们的参数类型。
interface ILength {
length: number
}
// 泛型继承接口, 这样传入的泛型类型就必须和ILength接口一样具有length属性才可以
function getLength<T extends ILength>(arg: T) {
console.log(arg.length)
}
// getLength(123) // 123没有length无法传入
getLength("abcdefg") // 7
getLength([10, 20, 30, 40]) // 4
getLength({ length: 20 }) // 10
T extends表示传入的类型必须是extends后面的类型或者子类型。