TypeScript学习之泛型(2)

泛型类型

实际上可以理解为,将泛型提取出来,作为一个通用类型。
通常,对于泛型函数的类型使用,可能是以下这种方式:(类型变量不一定是T,也可以是其他的名字)

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: <T>(arg: T) => T = identity;
复制代码

这种写法其实看上去会有点像js中使用箭头函数的形式去声明一个函数。

在TypeScript中,可以将箭头函数部分提取出来,作为一个接口,也就是泛型接口:

interface GenericIdentityFn {
    <T>(arg: T): T;
}

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: GenericIdentityFn = identity;
复制代码

如果把泛型参数当作整个接口的一个参数,即上述例子中GenericIdentityFn的参数T,这样就能清楚的知道使用的具体是哪个泛型类型,比如说,传入一个number:

interface GenericIdentityFn<T> {
    (arg: T): T
}

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: GenericIdentityFn<number> = identity;
复制代码

上述例子中,给接口传入了一个参数number,在使用的时候,就意味着使用的时候,这里的泛型类型就是number。

泛型类

除了泛型接口之外,还可以创建泛型类,和泛型接口差不多,泛型类使用<>包裹泛型类型,跟在类名后面。

class GenericNumber<T> (
    zeroValue: T;
    add: (x: T, y: T) => T
)

let mynericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {
    return x + y;
}
复制代码

()中是泛型类型的参数列表,对于泛型类型,不一定是number,这也是个传参,同样可以传string等。像接口一样,将泛型类型放在类本身,可以确保类的所有属性都使用想用的类型,保证了类型的统一。

泛型约束

在有些情况下,编译器不能保证每种类型都会有某个属性存在,例如说number类型不存在length属性,但是又会需要去处理,相对于any,限制函数的处理或许是更好的办法。那么,就需要传入的类型中,有这个属性存在,因此,这便对泛型变量T产生了一个约束的概念。

定义一个接口来描述约束条件:

interface Lengthwise {
    length: number;
}

function loggingItentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}
复制代码

当泛型函数被定义了约束,便不再适用于任意类型。在使用中,必须传入符合约束类型的值:

loggingIdentity(3);  // Error, number doesn't have a .length property

loggingIdentity({length: 10, value: 3})
复制代码

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

可以声明一个类型参数,且它被另一个类型参数所约束。举个例子来说,想从一个对象中获取一个属性,但是又需要确保取到的属性是存在的,这是,可以使用另外一个类型参数来约束这个行为。

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'.
复制代码

在泛型里使用类类型

class BeeKeeper {
    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}

createInstance(Lion).keeper.nametag;  // typechecks!
createInstance(Bee).keeper.hasMask;   // typechecks!
复制代码

おすすめ

転載: juejin.im/post/7032610487372251150