类型操作之Generics泛型

107 阅读2分钟

概论

TS-类型操作.png

为什么要使用泛型

创建可重用的组件,一个组件可以支持多种类型的数据。

泛型理解

Generics - Types which take parameters 相当于传入到function | interface | class中一个参数

泛型使用

基础定义

  • 有一个类型参数在最前面,像函数声明一样 元素形式:
  //demo1
  function identity<Type>(arg: Type): Type {
    return arg;
  }
  //泛型参数名可自己定义,下面示例与上面等同
  function identity<T>(arg: T): T {
    return arg;
  }

数组形式:

  //demo2
  function loggingIdentity<Type>(arg: Type[]): Type[] {
    console.log(arg.length);
    return arg;
  }
  //也可以使用下面的形式,但是上面的形式更为推荐
  function loggingIdentity<T>(arg: Array<T>): Array<T> {
      console.log(arg.length);  // Array has a .length, so no more error
      return arg;
  }
  • 可以使用不同的泛型参数名,如demo1所示
  • 可以使用带有调用签名的对象字面量来定义泛型函数,如demo3中 {<T>(arg: T): T}
//demo4
function identity<T>(arg: T): T {
    return arg;
}
​
let myIdentity: {<T>(arg: T): T} = identity;

泛型接口 Generic Interface

在demo4的例子中,将带有调用签名的对象字面量提取成接口

//demo5
interface GenericIdentityFn {
    <T>(arg: T): T; //函数形式接口,传入参数arg,返回同类型值
}
​
function identity<T>(arg: T): T {
    return arg;
}
​
let myIdentity: GenericIdentityFn = identity;

如果把泛型参数当做interface的一个参数,则:

//demo6
interface GenericIdentityFn<T> {
    (arg: T): T;
}
​
function identity<T>(arg: T): T {
    return arg;
}
​
let myIdentity: GenericIdentityFn<number> = identity;

泛型类 Generic Classes

同泛型接口差不多。 泛型类使用( <>)括起泛型类型,跟在类名后面

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}
​
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

泛型约束 Generic Constraints

  • 普通泛型约束

把将要传入的泛型,继承自某个interface | class,对泛型进行约束,如:

interface Lengthwise {
    length: number;
}
​
function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}
loggingIdentity(3);  // Error, number doesn't have a .length property
loggingIdentity({length: 10, value: 3}); // Now we know it has a .length property, so no more error
  • 在泛型约束中使用类型参数

如下demo所示:通过keyof的解析实现对K的约束限制(keyof的理解可参见下一篇)

function getProperty<T, K extends keyof T>(obj: T, key: K) {
    console.log(obj[key]);
    return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };   
getProperty(x, "a");
getProperty(x, "m"); //Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.

泛型中使用类类型(Using Class Types in Generics)

{ new (): Type }其构造函数返回值是泛型Type

function create<Type>(c: { new (): Type }): Type {
  return new c();
}

如下demo所示:对c: new () => A的理解

class BeeKeeper {
    hasMask: boolean = true;
}
class ZooKeeper {
    nametag: string = "Mikle";
}
class Animal {
    numLegs: number = 4;
}
class Bee extends Animal {
    keeper: BeeKeeper = new BeeKeeper();
}
class Lion extends Animal {
    keeper: ZooKeeper = new ZooKeeper();
}
/**
传入的泛型是:A extends Animal
对"c: new () => A"理解:
createInstance参数c必须是这样的一个类:
其构造函数返回值是传入的泛型A
*/
function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}
createInstance(Animal).numLegs; //4  
createInstance(Lion).keeper.nametag; //Mikle
createInstance(Bee).keeper.hasMask; //true