最近开发遇到了 ts 泛型,但发现自己已经忘的差不多了,于是回头复习一下 ts 泛型的知识,并做一些笔记的总结分享~
你将学到:
- 泛型是什么?为什么要用泛型?
- 泛型如何使用
- 泛型部分实用工具类型
泛型是什么?为什么要用泛型?
引用官网的例子,此时有一个需求:我们要定义一个函数,他会返回任何传入他的值。
这个情况下,我们如果已知他的数据类型(假定是 number),就可以写出以下代码:
function echo(val: number): number {
return val
}
但如果我们不知道他的类型,那么我们只能使用 any类型
function echo(val: any): any {
return val
}
显然,使用any会使我们丢失一些信息,例如我们已经没有办法保证输入输出的值类型相同,因为他们的值都可以是任何类型。
所以这种时候我们就要使用泛型,即类型变量。它是表示类型的变量,而不是一个值。
泛型如何使用
在函数名之后写上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属性
泛型部分实用工具类型
-
Exclude
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
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
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
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 }
结语
以上是关于 ts 泛型的笔记心得总结,如有错误欢迎在评论区指出~