在学习这一节之前,你需要了解 接口 以及 这两节的内容

实现接口的类

实现( implements )是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口( interfaces ),用 implements 关键字来实现。这个特性大大提高了面向对象的灵活性。
举例来说,门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,我们可以简单的给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它。

  1. interface ClockInterface {
  2. currentTime: Date;
  3. }
  4. class Clock implements ClockInterface {
  5. public currentTime: Date = new Date();
  6. constructor(h: number, m: number) { }
  7. }

也可以在接口中描述一个方法,在类里实现它,如同下面的setTime方法一样:

  1. interface ClockInterface {
  2. currentTime: Date;
  3. setTime(d: Date): void;
  4. }
  5. class Clock implements ClockInterface {
  6. public currentTime: Date = new Date();
  7. constructor(h: number, m: number) { }
  8. public setTime(d: Date) {
  9. this.currentTime = d;
  10. }
  11. }

接口描述了类的公共部分,而不是公共和私有两部分。 它不会帮你检查类是否具有某些私有成员。

一个类可以实现多个接口

  1. interface ClockInterface {
  2. currentTime: Date;
  3. setTime(d: Date): void;
  4. }
  5. interface LogTime {
  6. log(): void;
  7. }
  8. class Clock implements ClockInterface, LogTime {
  9. public currentTime: Date = new Date();
  10. constructor(public h: number, public m: number) { }
  11. public setTime(d: Date) {
  12. this.currentTime = d;
  13. }
  14. public log() {
  15. console.log(this.currentTime);
  16. }
  17. }

接口的继承

和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。

  1. interface Shape {
  2. color: string;
  3. }
  4. interface Square extends Shape {
  5. sideLength: number;
  6. }
  7. let square = {} as Square;
  8. square.color = "blue";
  9. square.sideLength = 10;

一个接口可以继承多个接口

  1. interface Shape {
  2. color: string;
  3. }
  4. interface PenStroke {
  5. penWidth: number;
  6. }
  7. interface Square extends Shape, PenStroke {
  8. sideLength: number;
  9. }
  10. let square = {} as Square;
  11. square.color = "blue";
  12. square.sideLength = 10;
  13. square.penWidth = 5.0;

接口继承类

当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的 privateprotected 成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现( implement)。
当你有一个庞大的继承结构时这很有用,但要指出的是你的代码只在子类拥有特定属性时起作用。 除了继承自基类,子类之间不必相关联。

  1. class Control {
  2. protected state: any;
  3. }
  4. interface SelectableControl extends Control {
  5. select(): void;
  6. }
  7. class Button extends Control implements SelectableControl {
  8. public select() {
  9. console.log("select");
  10. }
  11. }
  12. class TextBox extends Control {
  13. public select() {
  14. console.log("select");
  15. }
  16. }
  17. class Images extends Control implements SelectableControl {
  18. constructor() {
  19. super();
  20. }
  21. public select() {
  22. console.log(this.state);
  23. }
  24. }

在上面的例子里,SelectableControl包含了Control的所有成员,包括私有成员state。 因为state是私有成员但可被子类访问,所以只能够是Control的子类们才能实现SelectableControl接口。 因为只有Control的子类才能够拥有一个声明于Control的私有成员state,这对私有成员的兼容性是必需的。 在Control类内部,是允许通过SelectableControl的实例来访问私有成员state的。 实际上,SelectableControl就像Control一样,并拥有一个select方法。 ButtonTextBox类是SelectableControl的子类(因为它们都继承自Control并有select方法)

这里其实我将官网的例子加以改造了,按照官网的例子是这样的:

  1. class Control {
  2. private state: any;
  3. }
  4. interface SelectableControl extends Control {
  5. select(): void;
  6. }
  7. class Button extends Control implements SelectableControl {
  8. select() { }
  9. }
  10. class TextBox extends Control {
  11. select() { }
  12. }
  13. // Error: Property 'state' is missing in type 'Image'.
  14. class Image implements SelectableControl {
  15. select() { }
  16. }
  17. class Location {
  18. }

在这个例子里,state作为一个私有属性,是不可以被声明类以外的类访问的。

如果以上的例子都过于复杂的话,这里提供一个相对简单的例子,这个例子也可以看作是一个将类当作接口使用的例子:

  1. class Point {
  2. public x: number;
  3. public y: number;
  4. }
  5. interface Point3D extends Point {
  6. z: number;
  7. }
  8. let point3d: Point3D = {x: 1, y: 2, z: 3};

混合使用

我们在之前的学习中知道了,我们可以使用接口的方式来定义一个函数需要符合的形状:

  1. type SearchFunc = (source: string, subString: string) => boolean;
  2. let mySearch: SearchFunc;
  3. mySearch = (source: string, subString: string) => {
  4. return source.search(subString) !== -1;
  5. };
  6. console.log(mySearch("zxc", "z"));

有时候,一个函数还可以有自己的属性和方法:

  1. interface Counter {
  2. (start: number): string;
  3. interval: number;
  4. reset(): void;
  5. }
  6. function getCounter(): Counter {
  7. const fun: (start: number) => string = (starts: number): string => {
  8. return starts.toString();
  9. };
  10. const fun2: () => void = (): void => {
  11. console.log("void func");
  12. };
  13. const counter = fun as Counter;
  14. counter.interval = 123;
  15. counter.reset = fun2;
  16. return counter;
  17. }
  18. const c = getCounter();
  19. c(10);
  20. c.reset();
  21. c.interval = 5.0;