最近回头复习了一下 ts 泛型的知识,做一些笔记的总结分享~
1.泛型是什么?为什么要用泛型?
引用官网的例子,此时有一个需求:我们要定义一个函数,他会返回任何传入他的值。
这个情况下,我们如果已知他的数据类型(假定是 number
),就可以写出以下代码:
function echo(val: number): number {
return val
}
但如果我们不知道他的类型,那么我们只能使用 any
类型
function echo(val: any): any {
return val
}
显然,使用any
会使我们丢失一些信息,例如我们已经没有办法保证输入输出的值类型相同,因为他们的值都可以是任何类型。
所以这种时候我们就要使用泛型,即类型变量。它是表示类型的变量,而不是一个值。
2.泛型如何使用
在函数名之后写上fn<泛型变量名>
,通常泛型名使用大写的 T,当然你可以随意命名,在调用的时候可以在函数名字后面加上fn<泛型类型>
使用泛型后,我们可以这样改写上面的echo
函数:
function echo<T>(val: T): T {
return val;
}
我们给 echo
添加了一个类型变量T
,这样就能实现在初始类型未知的同时,保证输入输出值的类型相同了。
使用echo
函数:
const output = echo<number>(1)
前面我们说到,在调用的时候函数名字后面可以加上<泛型类型>
但有时候,我们没必要使用<>
明确的传入类型,编译器可以查看传入的值,来进行自动推断,然后把T
自动设置为他的类型。
所以上面的代码可以改写成:
const output = echo(1)
我们也可以使用不同的泛型参数名,只要在数量上和使用方式上能对应上就可以。
let myEcho: <U>(val: U) => U = echo;
泛型类
泛型类与泛型接口类似:
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let testGenericNumber = new GenericNumber<number>()
testGenericNumber = 0
testGenericNumber.add = function(x, y) {
return x + y
}
泛型类的使用是非常直观的,上面的例子中,我们限定了T
的值为number
,当然也可以使用其他类型。
泛型约束
书写泛型的时候,我们往往需要对类型参数做一些约束。此时我们要使用extends
关键字。
如下面的例子:
interface Phone {
phone: string
}
function getPhone<T extends Phone>(val: T): string {
return val.phone
}
// 使用函数
getPhone({
name: "小明",
phone: '',
age: 10
})
这里写了一个获取手机号的方法,对传入参数的类型做了限制,数组的每一项可以是包含多个属性的对象,但其必须有一个string
类型的phone
属性
3.泛型部分实用工具类型
Exclude<T, U>
:他的作用是将T
里面的U
去掉,并返回T
,主要用于联合类型。
Exclude
的定义是type Exclude <T, U> = T extends U ? never : T
例子:
type A = Exclude<string | number, number> // string
type B = Exclude<'key1' | 'key2', 'key2' | 'key3'> // 'key1'
由定义得原理:
type A = Exclude<string | number, number>
// 等价于
type A = Exclude<string, number> | Exclude<number, number>
// =>
type A = (string extends number ? never : string) | (number extends number ? never : number)
// =>
type A = string | never
// 又因为 never 是所有类型的子类型
type A = string
Extract<T, U>
:他的作用与上面的Exclude
相反,是将T
里面的U
取出,并返回,主要用于联合类型。
Extract
的定义是type Extract<T, U> = T extends U ? T : never
,可以知道,原理和Exclude
一样,只是调换了位置。
例子:
type A = Exclude<'a' | 'b' | 'c', 'a' | 'd'> //'a'
Omit<T, U>
:作用是把 T(对象类型)
里边的 U
去掉,并返回 T
里还剩下的。而他和Exclude
的区别在于,Exclude
多用于联合类型,而Omit
多用于处理对象类型。
Omit
的定义:type Omit <T, K extends keyof any> = { [P in Exclude <keyof T, K>]: T[P] }
例子:
type userInfo = {
name: string
age: number
phone: number
}
type A = Omit<userInfo, 'age'> //type A = { name: string, phone: number }
Pick<T, U>
:顾名思义,它的作用是把 T(对象类型)
里边的 U
提取出来并返回,很明显,与Omit
呈相反的关系。
Pick
的定义:type Pick<T, K extends keyof T> = { [P in K]: T[P] }
其中K extends keyof T
则是用来约束K的条件,即,传入K
的参数必须是T
中有的类型。
类似Omit
的例子:
type userInfo = {
name: string
age: number
phone: number
}
type A = Pick<userInfo, 'name' | 'phone'> //type A = { name: string, phone: number }
4.结语
以上是复习 ts 泛型时的笔记心得,如有错误欢迎在评论区指出~