概念:泛型(Generics)是指在定义函数、接口或者类的时候, 不预先指定其类型,而是在使用是手动指定其类型的一种特性

1、使用场景

我们需要创建一个函数, 这个函数会返回任何它传入的值。

  1. function identity (arg: any): any {
  2. return arg
  3. }
  4. identity (3) // => 3

这样代码编译不会出错,但是存在一个显而易见的缺陷,就是没办法约束输出的类型与输入的类型保持一致

这时可以用泛型来解决这个问题

  1. function identity<T>(arg: T): T {
  2. return arg
  3. }
  4. identity(3) // => 3

上例中我们在函数名字后面价一个,其中的 T 表示任意输入的类型,后面的 T 即表示输出的类型,且与输入 保持一致

当然我们也可以在调用时手动指定输入与输出的类型, 如上述函数指定

  1. identity<number>(3) // => 3

2、泛型约束

在泛型函数内部使用类型变量时, 由于事先并不知道它是那种类型, 所以不能随意操作它的属性和方法

  1. function identity<T>(ary: T): T {
  2. console.log(arg.length) // => err
  3. return arg
  4. }

上述函数中 类型 T 上不一定存在 length 属性, 所以编译的时候就报错了。

这时,我们可以的对泛型进行约束,对这个函数传入的值约束必须包含 length 的属性, 这就是泛型约束

  1. interface lengthwise {
  2. length: number
  3. }
  4. function identity<T extends lengthwise> (arg: T): T {
  5. console.log(arg.length) // => err
  6. return arg
  7. }
  8. identity({a: 1, length: 1}) // => 1
  9. identity('str') // => 3
  10. identity(6) // => err 传入是参数中未能包含length属性

这样我们就可以通过泛型约束的方法对函数传入的参数进行约束限制。

多个参数时也可以在泛型约束中使用类型参数
如你声明了一个类型参数, 它被另一类型参数所约束。现在想要用属性名从对象里湖区这个属性。并且还需确保这个属性存在于这个对象上, 因此需要咋这两个类型之间使用约束,
简单举例来说: 定义一个函数, 接受两个参数 第一个是个对象 obj,第二个个参数是第一参数 key 是对象里面的键名, 需要输入obj[key]

  1. function identity <T, K extends keyof T>(obj: T, key: K) {
  2. return obj[key]
  3. }
  4. let obj = { a: 1, b: 2, c: 3 }
  5. identity(obj, 'a') // => success
  6. identity(obj, 'm') // => err obj 中不存在 m 这个参数

3、泛型接口

之前学过可以使用接口的方式来定义一个函数需要符合的形状

  1. interface SearchFunc {
  2. (source: string, subString: string): boolean;
  3. }
  4. let mySearch: SearchFunc;
  5. mySearch = function(source: string, subString: string) {
  6. return source.search(subString) !== -1;
  7. }

当然也可以使用含有泛型的接口来定义函数的形状

  1. interface CreateArrayFunc {
  2. <T>(length: number, value: T): Array<T>;
  3. }
  4. let createArray: CreateArrayFunc;
  5. createArray = function<T>(length: number, value: T): Array<T> {
  6. let result: T[] = [];
  7. for (let i = 0; i < length; i++) {
  8. result[i] = value;
  9. }
  10. return result;
  11. }
  12. createArray(3, 'x'); // => ['x', 'x', 'x']

进一步,我们可以把泛型参数提前到接口名上

  1. interface CreateArrayFunc<T> {
  2. (length: number, value: T): Array<T>;
  3. }
  4. let createArray: CreateArrayFunc<any>;
  5. createArray = function<T>(length: number, value: T): Array<T> {
  6. let result: T[] = [];
  7. for (let i = 0; i < length; i++) {
  8. result[i] = value;
  9. }
  10. return result;
  11. }
  12. createArray(3, 'x'); // => ['x', 'x', 'x']