hackftz

bits change world

导航

深入理解TypeScript——文档篇之泛型

Posted on 2020-10-13 22:28  hackftz  阅读(56)  评论(0编辑  收藏  举报

一、介绍

类型变量,它是一种特殊的变量,只用于表示类型而不是值。

function identity<T>(arg: T): T { // 声明带有泛型的函数
    return arg;
}

二、使用

  1. 传入所有的参数,包含类型参数:

    let output = identity<string>("myString");  // type of output will be 'string'
    
  2. 类型推论

    let output = identity("myString");  // type of output will be 'string'
    

    编译器可以查看myString的值,然后把T设置为它的类型。 类型推论帮助我们保持代码精简和高可读性。如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入T的类型,在一些复杂的情况下,这是可能出现的。

三、类型

  1. 类型参数在最前面。

    function identity<T>(arg: T): T {
        return arg;
    }
    
    let myIdentity: <T>(arg: T) => T = identity;
    
  2. 可以使用不同的泛型参数名。

    function identity<T>(arg: T): T {
        return arg;
    }
    
    let myIdentity: <U>(arg: U) => U = identity;
    
  3. 使用带有调用签名的对象字面量来定义。

    function identity<T>(arg: T): T {
        return arg;
    }
    
    let myIdentity: {<T>(arg: T): T} = identity;
    
    interface GenericIdentityFn {
        <T>(arg: T): T;
    }
    
    function identity<T>(arg: T): T {
        return arg;
    }
    
    let myIdentity: GenericIdentityFn = identity;
    
  4. 泛型接口参数。

    // 把参数放在调用签名里
    interface GenericIdentityFn {
        <T>(arg: T): T; 
    }
    
    function identity<T>(arg: T): T {
        return arg;
    }
    
    let myIdentity: GenericIdentityFn = identity;
    
    // 把参数放在调用接口上
    interface GenericIdentityFn<T> { 
        (arg: T): T;
    }
    
    function identity<T>(arg: T): T {
        return arg;
    }
    
    let myIdentity: GenericIdentityFn<number> = identity; // 锁定了之后代码里使用的类型
    

    除了泛型接口,我们还可以创建泛型类。
    注意,无法创建泛型枚举和泛型命名空间。

四、泛型类

泛型类使用( <>)括起泛型类型,跟在类名后面。
```ts
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; };
```  

注意:泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型。
这里意思是泛型类约束的是除类静态部分其他的属性部分。

五、泛型约束

  1. 限制传入参数的类型。

    interface Lengthwise { // 创建一个包含 .length属性的接口
        length: number;
    }
    
    function loggingIdentity<T extends Lengthwise>(arg: T): T { // 使用这个接口和extends关键字来实现约束
        console.log(arg.length);  // Now we know it has a .length property, so no more error
        return arg;
    }
    
  2. 在泛型约束中使用类型参数
    两个类型之间使用约束

    function getProperty(obj: T, key: K) {
        return obj[key];
    }
    
    let x = { a: 1, b: 2, c: 3, d: 4 };
    
    getProperty(x, "a"); // okay
    getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.
    
  3. 在泛型里使用类类型
    (1) 工厂函数

    function create<T>(c: {new(): T; }): T {
        return new c();
    }
    
    // test
    class Test {
        name: string = 'test';
    }
    
    let a = create(Test)
    
    console.log(a, 123);
    

    (2) 原型属性推断并约束构造函数与类实例的关系

    class BeeKeeper {
      hasMask: boolean = false;
    }
    
    class ZooKeeper {
        nametag: string = '333';
    }
    
    class Animal {
        numLegs: number = 1;
    }
    
    class Bee extends Animal {
        keeper: BeeKeeper = new BeeKeeper();
    }
    
    class Lion extends Animal {
        keeper: ZooKeeper = new ZooKeeper();
    }
    
    function createInstance<A extends Animal>(c: new () => A): A {
        return new c();
    }
    
    var a = createInstance(Lion).keeper.nametag;  // typechecks!
    // createInstance(Bee).keeper.hasMask;   // typechecks!
    
    console.log(a, 123);
    
    function create<T>(c: {new(): T; }): T {
      return new c();
    }