什么是泛型?
"泛"就是广泛的意思,"型"就是数据类型。顾名思义,泛型就是适用于多种数据类型的一种类型。
泛型的作用
它能够帮助我们构建出复用性更强的代码
function getResult(value: number): number {
return value
}
例如,我们希望传参类型和函数返回类型一致时。我们可以使用泛型。
使用泛型
function getResult<T>(value: T): T {
return value
}
在定义一个函数时,不决定这些参数的类型,而是让调用者以参数的形式告知函数参数应该是什么类型。
<T>里的T便是TS的类型变量,也就是把T看做额外的一个参数,把类型参数化。
怎么传入泛型
函数定义时,<>的位置就是泛型的传入位置,比如上面函数的<T>。
getResult<number>(100) // 泛型收缩为数字
sum<{name: string}>({ name: "zs" }) // 泛型收缩为引用对象
或者我们可以让TS通过类型推导 自动识别:
getResult(100)
sum({ name: "zs" })
常用的泛型类型
- T:Type的缩写,类型
- K、V:key和value的缩写,键值对
- E:Element的缩写,元素
- O:Object的缩写,对象
泛型类型不是一定要使用T来表示类型。只是大家常用T表示类型。
TS允许引入希望定义的任何名字的类型变量。比如我们引入一个新的类型变量 U,重写上面getResult方法。
function getResult<U>(val:U):U{return val}
getResult<number>(100)
同时引入希望定义的任何数量的类型变量
function sumNew<U,A>(val:U,count:A):number{return 300}
sumNew(3,'ss')
泛型与接口
在定义接口的时候也可以使用泛型来对接口内的成员进行类型约束。
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属性的参数,该怎么写?
定义一个接口,指定length属性
interface hasLength {
length: number
}
用一个继承hasLength的接口做泛型约束:
function fn<T> (arg:T) {
console.log(arg)
}
function fn<T extends hasLength> (arg:T) {
console.log(arg)
}