taizuduojie 2019-09-08
有了泛型之后,一个函数或容器类能处理的类型一下子扩到了无限大,似乎有点失控的感觉。所以这里又产生了一个约束的概念。我们可以声明对类型参数进行约束。
我们还拿上文中的student栗子来说,想访问value的length属性,但是编译器并不能证明每种类型都有length属性,所以就报错了。
student = <T extends {}>(value: T): T => { //Property 'length' does not exist on type 'T'.ts(2339) console.log(value.length); return value; }
相比于操作any
所有类型,我们想要限制函数去处理任意带有.length
属性的所有类型。 只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性。 为此,我们需要列出对于T的约束要求。为此,我们定义一个接口来描述约束条件。 创建一个包含 .length
属性的接口,使用这个接口和extends
关键字来实现约束:
interface LengthDefine { length: number; } //这样函数定义是编译器就不会报错了 student = <T extends LengthDefine>(value: T): T => { console.log(value.length); return value; }
调用:
传入的是字符串能,里面含有length属性,能正常运行
let name: string = this.student<string>(itemFun.getName('Jack'));
如果传入的是number类型的数字:
let age = this.student(10); //Argument of type '10' is not assignable to parameter of type 'LengthDefine'.
我们需要传入符合约束类型的值,必须包含必须的属性:
let name: string = this.student<string>('Jack'); let age = this.student({length: 1, value: 10}); age的类型推导结果: (property) Init.student: <{ length: number; value: number; }>(value: { length: number; value: number; }) => { length: number; value: number; }
我们可以声明一个类型参数,且它被另一个类型参数所约束。 比如,现在我们想要用属性名从对象里获取这个属性。 并且我们想要确保这个属性存在于对象 obj上,因此我们需要在这两个类型之间使用约束。
getProperty = <T, K extends keyof T>(obj: T, key: K) => { return obj[key] } let obj = {a:1, b:"2", c:3}; console.log(this.getProperty(obj, 'a')); //1 console.log(this.getProperty(obj, 'd')); //Argument of type '"d"' is not assignable to parameter of type '"a" | "b" | "c"'.ts(2345)
在TypeScript使用泛型创建工厂函数时,需要引用构造函数的类类型。比如:
function create<T> (c: {new(): T;}): T { return new c(); }
使用原型属性推断并约束构造函数与类实例的关系
class Keeper1 { hasMask: boolean; } class Keeper2 { nameTag: string; } class Keeper3 { numLength: number; } class ChildrenKeeper1 extends Keeper3 { keeper: Keeper1; } class ChildrenKeeper2 extends Keeper3 { keeper: Keeper2; } function createInstance<A extends Keeper3> (c: new() => A): A { return new c(); } console.log(createInstance(ChildrenKeeper1)); //ChildrenKeeper1 {} console.log(createInstance(ChildrenKeeper2)); //ChildrenKeeper2 {}