TypeScript高级用法的知识点汇总(4)

然而TypeScript会编译出错,告诉我们错误地实现了HumanConstructor接口,这是因为当一个类实现一个接口时,只会对实例部分进行编译检查,类的静态部分是不会被编译器检查的。因此这里我们尝试换种方式,直接操作类的静态部分,示例如下:

interface HumanConstructor { new (name: string, age: number); } interface IHuman { name: string; age: number; walk(): void; } class Human implements IHuman { public constructor(public name: string, public age: number) { this.name = name; this.age = age; } walk(): void { console.log('I am walking...'); } } // 定义一个工厂方法 function createHuman(constructor: HumanConstructor, name: string, age: number): IHuman { return new constructor(name, age); } const man = createHuman(Human, 'tom', 18); console.log(man.name, man.age); // -> tom 18

在上述示例中通过额外创建一个工厂方法createHuman并将构造函数作为第一个参数传入,此时当我们调用createHuman(Human, 'tom', 18)时编译器便会检查第一个参数是否符合HumanConstructor接口的构造器签名。

4、声明合并

在声明合并中最常见的合并类型就是接口了,因此这里先从接口开始介绍几种比较常见的合并方式。

4.1 接口合并

示例代码如下:

interface A { name: string; } interface A { age: number; } // 等价于 interface A { name: string; age: number; } const a: A = {name: 'tom', age: 18};

接口合并的方式比较容易理解,即声明多个同名的接口,每个接口中包含不同的属性声明,最终这些来自多个接口的属性声明会被合并到同一个接口中。

注意:所有同名接口中的非函数成员必须唯一,如果不唯一则必须保证类型相同,否则编译器会报错。对于函数成员,后声明的同名接口会覆盖掉之前声明的同名接口,即后声明的同名接口中的函数相当于一次重载,具有更高的优先级。

4.2 函数合并

函数的合并可以简单理解为函数的重载,即通过同时定义多个不同类型参数或不同类型返回值的同名函数来实现,示例代码如下:

// 函数定义 function foo(x: number): number; function foo(x: string): string; // 函数具体实现 function foo(x: number | string): number | string { if (typeof x === 'number') { return (x).toFixed(2); } return x.substring(0, x.length - 1); }

在上述示例中,我们对foo函数进行多次定义,每次定义的函数参数类型不同,返回值类型不同,最后一次为函数的具体实现,在实现中只有在兼容到前面的所有定义时,编译器才不会报错。

注意:TypeScript编译器会优先从最开始的函数定义进行匹配,因此如果多个函数定义存在包含关系,则需要将最精确的函数定义放到最前面,否则将始终不会被匹配到。

4.3 类型别名联合

类型别名联合与接口合并有所区别,类型别名不会新建一个类型,只是创建一个新的别名来对多个类型进行引用,同时不能像接口一样被实现(implements)和继承(extends),示例如下:

type HumanProperty = { name: string; age: number; gender: number; }; type HumanBehavior = { eat(): void; walk(): void; } type Human = HumanProperty & HumanBehavior; let woman: Human = { name: 'tom', age: 18, gender: 0, eat() { console.log('I can eat.'); }, walk() { console.log('I can walk.'); } } class HumanComponent extends Human { constructor(public name: string, public age: number, public gender: number) { this.name = name; this.age = age; this.gender = gender; } eat() { console.log('I can eat.'); } walk() { console.log('I can walk.'); } } // -> 'Human' only refers to a type, but is being used as a value here.

5、keyof 索引查询

在TypeScript中的keyof有点类似于JS中的Object.keys()方法,但是区别在于前者遍历的是类型中的字符串索引,后者遍历的是对象中的键名,示例如下:

interface Rectangle { x: number; y: number; width: number; height: number; } type keys = keyof Rectangle; // 等价于 type keys = "x" | "y" | "width" | "height"; // 这里使用了泛型,强制要求第二个参数的参数名必须包含在第一个参数的所有字符串索引中 function getRectProperty<T extends object, K extends keyof T>(rect: T, property: K): T[K] { return rect[property]; } let rect: Rectangle = { x: 50, y: 50, width: 100, height: 200 }; console.log(getRectProperty(rect, 'width')); // -> 100 console.log(getRectProperty(rect, 'notExist')); // -> Argument of type '"notExist"' is not assignable to parameter of type '"width" | "x" | "y" | "height"'.

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/a5cd6f57eef1c0c30548e3ad454fcdf6.html