在之前的 对象的类型——接口 一文中知道可以定义接口来约束对象的类型。接口 ( Interfaces ) 是面向对象语言中的一个重要概念,它的用途是对类的一部分行为进行抽象,而具体的实现方法和动作需要通过类 ( Class ) 来实现 ( Implements ) 。

1 类实现接口

一般来讲,一个类只能继承另一个类,而有时候某些类之间存在一些共有的特性,这时可以将这些共有的特性抽象成一个接口,其它类通过实现 (Implements) 此接口来拥有此特性。 实现接口于继承类的区别是:一个类只能继承另一个类,但可以实现多个接口
如:门是一个大类,而防盗门是门的子类,防盗门拥有报警这个特性,同时汽车是一个类,汽车也有报警这个特性,那么可以将报警这个特性抽象为一个接口,让防盗门和汽车去实现此接口,此外汽车还有开灯与关灯功能:

  1. // 定义接口: 报警器
  2. interface Alarm {
  3. // 接口的特性:报警 (❗不能是具体的实现方法)
  4. alert(): void;
  5. }
  6. interface Light {
  7. lightOn(): void;
  8. lightOff(): void;
  9. }
  10. // 定义类:门
  11. class Door {}
  12. // 定义子类:安全门,继承于门,并实现报警器
  13. class SecurityDoor extends Door implements Alarm {
  14. alert(): void {
  15. console.log("SecurityDoor is alarming!");
  16. }
  17. }
  18. // 定义类:车,实现报警器和灯两个接口,用 `,` 隔开
  19. class Car implements Alarm, Light {
  20. alert(): void {
  21. console.log("Car is alarming!");
  22. }
  23. lightOn(): void {
  24. console.log("The Car's lights are on!");
  25. }
  26. // 实现的方法由自己决定,可更改返回值类型
  27. lightOff(): string {
  28. console.log("The Car's lights are off!");
  29. return "off";
  30. }
  31. }

❗ 注: EcmaScript 中不存在接口 ( Interfaces ) 定义, TypeScript 中的接口在编译过程中会删除接口相关的代码,只剩下类和类的方法实现。

2 接口继承接口

接口与接口之间也可以拥有继承关系,如车的报警器不仅能发出声音报警也能发光报警:

  1. // 接口继承接口
  2. interface Alarm {
  3. alert(): void;
  4. }
  5. interface LightableAlarm extends Alarm {
  6. lightOn(): void;
  7. lightOff(): void;
  8. }

3 接口继承类

常见的面向对象语言中,接口是不能继承类的,但在 TypeScript 中却可以

  1. // 接口继承类
  2. // 定义类
  3. class Point {
  4. x: number;
  5. y: number;
  6. constructor(x: number, y: number) {
  7. this.x = x;
  8. this.y = y;
  9. }
  10. }
  11. // 定义接口,继承一个类
  12. interface Point3D extends Point {
  13. z: number;
  14. }
  15. const p3d: Point3D = {x: 1, y:2, z: 3}

为什么在 TypeScript 中接口可以继承类呢?
这是因为在 TypeScript 中定义类的时候同时也定义了一个类的实例类型接口,因此我们可以用类来实例化对象,用类的实例类型接口来约束实例的类型。如:

  1. // 定义类
  2. class Point {
  3. x: number;
  4. y: number;
  5. constructor(x: number, y: number) {
  6. this.x = x;
  7. this.y = y;
  8. }
  9. }
  10. // 用类来实例化对象
  11. let p = new Point(1, 2);
  12. // 用类的实例类型接口来约束类型
  13. function printPoint(p: Point): void {
  14. console.log(p);
  15. }

上述过程可以解为

  1. // 定义类
  2. class Point {
  3. x: number;
  4. y: number;
  5. constructor(x: number, y: number) {
  6. this.x = x;
  7. this.y = y;
  8. }
  9. }
  10. // ❗同时定义类的实例类型接口
  11. interface PointInstanceType {
  12. x: number;
  13. y: number;
  14. }
  15. // 用类来实例化对象
  16. let p = new Point(1, 2);
  17. // ❗用类的实例类型接口来约束类型
  18. function printPoint(p: PointInstanceType): void {
  19. console.log(p);
  20. }

到这里就明白了为什么 TypeScript 中接口可以继承类了,如上述的 Point3D 的例子,可以解为

  1. // 接口继承类
  2. // 定义类
  3. class Point {
  4. x: number;
  5. y: number;
  6. constructor(x: number, y: number) {
  7. this.x = x;
  8. this.y = y;
  9. }
  10. }
  11. // ❗ 同时定义了一个类的实例类型接口
  12. interface PointInstanceType {
  13. x: number;
  14. y: number;
  15. }
  16. // 定义接口,继承一个类
  17. // ❗ 本质是接口继承类的实例类型接口 interface Point3D extends PointInstanceType
  18. interface Point3D extends Point {
  19. z: number;
  20. }
  21. const p3d: Point3D = {x: 1, y:2, z: 3}

所以 TypeScript 中的接口继承“类”,本质仍然是接口继承接口(类的实例类型接口),值得注意得是类的实例类型接口不会拥有构造函数、静态属性方法等,毕竟它只是一个接口