在之前的 对象的类型——接口 一文中知道可以定义接口来约束对象的类型。接口 ( Interfaces ) 是面向对象语言中的一个重要概念,它的用途是对类的一部分行为进行抽象,而具体的实现方法和动作需要通过类 ( Class ) 来实现 ( Implements ) 。
1 类实现接口
一般来讲,一个类只能继承另一个类,而有时候某些类之间存在一些共有的特性,这时可以将这些共有的特性抽象成一个接口,其它类通过实现 (Implements) 此接口来拥有此特性。 实现接口于继承类的区别是:一个类只能继承另一个类,但可以实现多个接口。
如:门是一个大类,而防盗门是门的子类,防盗门拥有报警这个特性,同时汽车是一个类,汽车也有报警这个特性,那么可以将报警这个特性抽象为一个接口,让防盗门和汽车去实现此接口,此外汽车还有开灯与关灯功能:
// 定义接口: 报警器interface Alarm {// 接口的特性:报警 (❗不能是具体的实现方法)alert(): void;}interface Light {lightOn(): void;lightOff(): void;}// 定义类:门class Door {}// 定义子类:安全门,继承于门,并实现报警器class SecurityDoor extends Door implements Alarm {alert(): void {console.log("SecurityDoor is alarming!");}}// 定义类:车,实现报警器和灯两个接口,用 `,` 隔开class Car implements Alarm, Light {alert(): void {console.log("Car is alarming!");}lightOn(): void {console.log("The Car's lights are on!");}// 实现的方法由自己决定,可更改返回值类型lightOff(): string {console.log("The Car's lights are off!");return "off";}}
❗ 注: EcmaScript 中不存在接口 ( Interfaces ) 定义, TypeScript 中的接口在编译过程中会删除接口相关的代码,只剩下类和类的方法实现。
2 接口继承接口
接口与接口之间也可以拥有继承关系,如车的报警器不仅能发出声音报警也能发光报警:
// 接口继承接口interface Alarm {alert(): void;}interface LightableAlarm extends Alarm {lightOn(): void;lightOff(): void;}
3 接口继承类
常见的面向对象语言中,接口是不能继承类的,但在 TypeScript 中却可以:
// 接口继承类// 定义类class Point {x: number;y: number;constructor(x: number, y: number) {this.x = x;this.y = y;}}// 定义接口,继承一个类interface Point3D extends Point {z: number;}const p3d: Point3D = {x: 1, y:2, z: 3}
为什么在 TypeScript 中接口可以继承类呢?
这是因为在 TypeScript 中定义类的时候同时也定义了一个类的实例类型接口,因此我们可以用类来实例化对象,用类的实例类型接口来约束实例的类型。如:
// 定义类class Point {x: number;y: number;constructor(x: number, y: number) {this.x = x;this.y = y;}}// 用类来实例化对象let p = new Point(1, 2);// 用类的实例类型接口来约束类型function printPoint(p: Point): void {console.log(p);}
上述过程可以解为
// 定义类class Point {x: number;y: number;constructor(x: number, y: number) {this.x = x;this.y = y;}}// ❗同时定义类的实例类型接口interface PointInstanceType {x: number;y: number;}// 用类来实例化对象let p = new Point(1, 2);// ❗用类的实例类型接口来约束类型function printPoint(p: PointInstanceType): void {console.log(p);}
到这里就明白了为什么 TypeScript 中接口可以继承类了,如上述的 Point3D 的例子,可以解为
// 接口继承类// 定义类class Point {x: number;y: number;constructor(x: number, y: number) {this.x = x;this.y = y;}}// ❗ 同时定义了一个类的实例类型接口interface PointInstanceType {x: number;y: number;}// 定义接口,继承一个类// ❗ 本质是接口继承类的实例类型接口 interface Point3D extends PointInstanceTypeinterface Point3D extends Point {z: number;}const p3d: Point3D = {x: 1, y:2, z: 3}
所以 TypeScript 中的接口继承“类”,本质仍然是接口继承接口(类的实例类型接口),值得注意得是类的实例类型接口不会拥有构造函数、静态属性方法等,毕竟它只是一个接口。
