聊聊TypeScript中類、介面之間相互繼承與實現的那些事兒

暖生發表於2019-01-11

本文講述了類的繼承、介面的實現中需要注意的一些小細節,同時也對類、介面的多繼承做了一些講解。

先看一下繼承

類的繼承

子類繼承了父類之後,就會將父類中定義的非 private 屬性以及方法都繼承下來

  class Animal {
      public name: string = "Animal";
      public age: number;

      sayHello() {
          console.log(`Hello ${ this.name }`);
      }
  }

  class Dog extends Animal {
      age: number;

      constructor(age) {
          super();
          this.age = age;
      }
  }

  const dog = new  Dog(6);

  dog.sayHello();
複製程式碼

由於 Dog 繼承了 Animal 類,所以同時也繼承了 Animal 的 name 屬性和 sayHello 方法,因此可以直接使用 dog 例項呼叫 sayHello 方法。 那麼問題來了:如果可以進行多繼承,若多個父類中都包含同一個屬性,那麼子類使用的應該是哪個父類的屬性呢? 因此 TypeScript 中不允許進行多繼承,可是我就是想進行類的多繼承該怎麼辦呢?可以使用用下節所說的 Mixins 的方式。

類的多繼承(Mixins 模擬)

  • 先定義兩個類,Person 和 Student 類
  // Person 類
  class Person {
      name: string;
      sayHello() {
          console.log('tag', `Helo ${ this.name }!`)
      }
  }

  // Student 類
  class Student {
      grade: number;

      study() {
          console.log('tag', ' I need Study!')
      }
  }
複製程式碼
  • 下面建立一個類,結合 Person 和 Student 這兩個類 首先應該注意到的是,沒使用 extends 而是使用 implements。 把類當成了介面,僅使用 Person 和 Student 的型別而非其實現。 我們可以這麼做來達到目的,為將要mixin進來的屬性方法建立出佔位屬性。 這告訴編譯器這些成員在執行時是可用的。
  class SmartObject implements Person, Student {

      // Person
      name: string = 'person';
      sayHello: () => void;
      // Activatable
      grade: number = 3;
      study: () => void;
  }
複製程式碼
  • 最後,把mixins混入定義的類,完成全部實現部分
  // 把mixins混入定義的類
  applyMixins(SmartObject, [Person, Student]);


  // applyMixins 方法
  function applyMixins(derivedCtor: any, baseCtors: any[]) {
      baseCtors.forEach(baseCtor => {
          Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
              derivedCtor.prototype[name] = baseCtor.prototype[name];
          })
      });
  }
複製程式碼

介面與類之間的繼承

介面繼承類的方式以及特點與上面類的繼承和類的多繼承一致。

介面與介面之間的多繼承

介面與介面之間是可以直接進行多繼承的。

  • 先定義兩個介面
  // 阿里介面
  interface Ali  {
      pay: () => void
  }

  // 騰訊介面
  interface Tencent {
      game: string
      play: () => void
  }
複製程式碼
  • 在定義一個介面繼承 Ali、Tencent 介面
  // 自己的介面
  interface Self extends Ali, Tencent {
      name: string
      say: () => void
  }
複製程式碼

此時Self 介面就包含了 Ali 和 Tencent 介面中所有的屬性和方法

  • 驗證一下 用一個類實現 Self 介面,必須要將 Ali、 Tencent、 Self 介面中包含的所有屬性和方法都宣告瞭才可以,不然會編譯報錯
  // 使用 Test 類實現 Self 介面
  class Test implements Self {
      game: string;
      name: string;
      pay() {
          console.log('經常用於支付');
      }
  
      play() {
          console.log('可以玩各種遊戲');
      }
  
      say() {
          console.log('不知道說點兒什麼');
      }
  }
複製程式碼

再來看一下介面的實現

介面的實現

介面在定義的時候,不能初始化屬性以及方法,屬性不能進行初始化,方法不能實現方法體。 類實現介面之後,必須宣告介面中定義的屬性以及方法。

  interface Animal {
      name: string;

      eat: () => void;
  }

  class Dog implements Animal {

      name: string;

      eat() {
          console.log('tag', 'I love eat bone!')
      }
  }

  const dog: Dog = new Dog();
  dog.eat();
複製程式碼

類對於介面的多實現

一個類可以實現多個介面,不過要將實現的所有介面的屬性和方法都實現了。

  // 動物介面
  interface Animal {
      name: string;

      eat: () => void;
  }

  // 貓科介面
  interface Felidae {
      claw: number;
      run: () => void;
  }

  // 讓貓類實現 Animal 和 Felidae 兩個介面
  class Cat implements Animal, Felidae {

      name: string;
      claw: number;

      eat() {
          console.log('tag', 'I love eat Food!');
      }

      run: () {
          console.log('tag', 'My speed is very fast!')
      }
  }

  const dog: Dog = new Dog();
  dog.eat();
複製程式碼

總結

  • 類與類之間只能進行單繼承,想要實現多繼承需要使用 Mixins 的方式

  • 介面繼承類也只能進行單繼承,想要實現多繼承需要使用 Mixins 的方式 Mixins 方式模擬多繼承的缺陷:

    • 只能在繼承一級父類的方法和屬性

    • 如果父類中含有同一種方法或屬性,會根據賦值的順序,先賦值的會被覆蓋掉

  • 介面與介面之間可以直接進行多繼承

  • 類實現介面可以進行多實現,每個介面用 , 隔開即可

相關文章