大家一起学泛型

391 阅读3分钟

TypeScript中,有个让人又爱又恨的玩意,相信学过的朋友都可以猜到,就是泛型(Generics)。接下来我们就一起来学习一下。

概念和作用

泛型,就是在定义函数、类或者接口的时候,不事先指定具体的类型,而是在使用的过程中再去具体确定类型的一种特性。在使用过程中,开发者可以自己去指定特性,有时也可以偷懒一下,依赖ts强大的类型推断功能。 接下来我就来介绍一下泛型在函数、接口、类中的具体应用。

在函数中的使用

function print(param1, param2): string {
  return `${param1}+${param2}`;
}

有这样一个场景,我们需要使print函数的两个参数的类型保持一致,但事先又不知道参数的类型,该怎么处理呢?解决此类问题,我们就可以求助于泛型。具体方法也很简单,只要在函数名后面加上一个泛型T即可。T可以理解为type类型的意思,当然用其他字母也可以,只是起到一个占位符的作用。

我们可以看到,调用print函数时,两个参数都变成了string类型。 我们可以再尝试一下,在调用含函数时,不在函数名后面传入指定的类型,会发生什么。比如:print("honor","super")。答案是,和之前是一样的。因为ts具有强大的类型推断机制,可以自己推断出传入参数的类型,然后推断出泛型T的类型。
我们可以再尝试一下,在函数名后面跟上两个类型参数.

function print<T, U>(param1, param2) {
  return `${param1}+${param2}`;
}
print<string, number>("honor", 12);

这个例子可以看到,我们成功把string和number类型赋到了两个参数上。

在接口中使用

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");

我的泛型变量的类型只能为string或者number类型,当传入boolean类型时,就会报错。

如果希望泛型变量带有name属性,

interface IName {
  name: string;
}

function print<T extends IName>(param: T) {
  return param;
}
print({
  name: "apple"
});

此时,如果函数参数中不含有name属性的话,同样是会报错的。

结语

最近几天一直在学习ts的知识,发现泛型还是挺难理解的。但是它的作用真的是很大,以后如果学习到新的用法,再进行补充,大家有使用心得,欢迎交流!