TypeScript中,有个让人又爱又恨的玩意,相信学过的朋友都可以猜到,就是泛型(Generics)。接下来我们就一起来学习一下。
概念和作用
泛型,就是在定义函数、类或者接口的时候,不事先指定具体的类型,而是在使用的过程中再去具体确定类型的一种特性。在使用过程中,开发者可以自己去指定特性,有时也可以偷懒一下,依赖ts强大的类型推断功能。 接下来我就来介绍一下泛型在函数、接口、类中的具体应用。
在函数中的使用
function print(param1, param2): string {
return `${param1}+${param2}`;
}
有这样一个场景,我们需要使print函数的两个参数的类型保持一致,但事先又不知道参数的类型,该怎么处理呢?解决此类问题,我们就可以求助于泛型。具体方法也很简单,只要在函数名后面加上一个泛型T即可。T可以理解为type类型的意思,当然用其他字母也可以,只是起到一个占位符的作用。
print("honor","super")。答案是,和之前是一样的。因为ts具有强大的类型推断机制,可以自己推断出传入参数的类型,然后推断出泛型T的类型。
function print<T, U>(param1, param2) {
return `${param1}+${param2}`;
}
print<string, number>("honor", 12);
在接口中使用
interface IParam<T> {
param1: T;
param2: string;
}
const info: IParam<number> = {
param1: 123,
param2: "super"
};
这里需要注意一点,在使用这个接口的时候。一定要在接口名后面跟上具体的类型值,否则是会报错的,这里不能依赖类型推断。
let numArr1: number[] = [1, 2];
let numArr2: Array<number> = [1, 2];
对于第二种写法,其实也是泛型在接口中的应用。Array就是一个接口,并且接收一个类型参数。当然这里也充分展示了interface功能的强大。当然了,大家平时尽量采用第一种方法定义数组。
泛型在类中的使用
class Fruit<T> {
constructor(private name: T) {}
printName() {
return this.name;
}
}
const apple = new Fruit<string>("chao");
具体的使用方法其实和函数和接口是一样的。在类名后面跟上对应的类型参数即可。
泛型约束
有时,我们定义了一个泛型变量,它的具体类型只有等到使用的过程中才知道,但是我们也可以对这个类型做一定的限制,比如将类型限制在string和number之间,这个就可以采用泛型约束,关键字为extends。
function print<T extends string | number>(param) {
return param;
}
print<string>("123");
如果希望泛型变量带有name属性,
interface IName {
name: string;
}
function print<T extends IName>(param: T) {
return param;
}
print({
name: "apple"
});
此时,如果函数参数中不含有name属性的话,同样是会报错的。
结语
最近几天一直在学习ts的知识,发现泛型还是挺难理解的。但是它的作用真的是很大,以后如果学习到新的用法,再进行补充,大家有使用心得,欢迎交流!