泛型类型
实际上可以理解为,将泛型提取出来,作为一个通用类型。
通常,对于泛型函数的类型使用,可能是以下这种方式:(类型变量不一定是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!
复制代码