TS泛型定义实例


定义泛型函数

  • 为了确保传入的类型与返回的类型相同,使用类型变量T表示传入参数和返回值的类型
    function identity<T>(arg: T): T {
      return arg
    }
  • 泛型函数两种调用方式:
    // 指定传入参数类型
    // 事实没必要使用尖括号(<>)来明确地传入类型
    let output = identity<string>("myString");
    // 较常用,使用 类型推论,推断传入类型和返回类型
    // 编译器可以查看myString的值,然后把T设置为它的类型
    let output = identity("myString");

使用泛型变量

  • 明确表示传入的参数为T类型的数组,确保参数有.length这个属性
    function loggingIdentity<T>(arg: T[]): T[] {
      console.log(arg.length);
      return arg;
    }
    // 也可以这样表示
    function loggingIdentity<T>(arg: Array<T>): Array<T> {
      console.log(arg.length);
      return arg;
    }

泛型类型

  • 泛型函数的类型,只需要将类型参数写在最前面,跟函数声明一样

    function identity<T>(arg: T): T {
      return arg;
    }
    
    let myIdentity: <T>(arg: T) => T = identity;
    // 也可以使用不同的泛型参数名,只要在数量上和使用方式上能对应上就可以
    let myIdentity2: <U>(arg: U) => U = identity;
  • 可以使用带有调用签名的对象字面量来定义泛型函数

    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;
    
  • 把泛型参数当作整个接口的一个参数,指定GenericIdentityFn的类型,这样接口里的其它成员也能知道这个参数的类型

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

泛型类

  • 泛型类看上去与泛型接口差不多。 泛型类使用( <>)括起泛型类型,跟在类名后面

    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; };
  • GenericNumber类的使用是十分直观的,并且你可能已经注意到了,没有什么去限制它只能使用number类型。 也可以使用字符串或其它更复杂的类型

    let stringNumeric = new GenericNumber<string>();
    stringNumeric.zeroValue = "";
    stringNumeric.add = function(x, y) { return x + y; };
    
    console.log(stringNumeric.add(stringNumeric.zeroValue, "test"));

泛型约束

  • 定义一个接口来描述约束条件。 创建一个包含.length属性的接口,使用这个接口和extends关键字来实现约束
    interface Lengthwise {
        length: number;
    }
    
    function loggingIdentity<T extends Lengthwise>(arg: T): T {
        console.log(arg.length);  
        return arg;
    }
    
    loggingIdentity("good") // √
    loggingIdentity(123) // ×
    loggingIdentity({length: 10, value: 3}) // √
    loggingIdentity({}) // ×
    loggingIdentity([]) // √

在泛型约束中使用类型参数

  • 声明一个类型参数,且它被另一个类型参数所约束。 比如,现在我们想要用属性名从对象里获取这个属性。 并且我们想要确保这个属性存在于对象 obj上,因此我们需要在这两个类型之间使用约束
    function getProperty(obj: T, key: K) {
        return obj[key];
    }
    
    let x = { a: 1, b: 2, c: 3, d: 4 };
    
    getProperty(x, "a");
    getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.

  目录