本文讲述了类的继承、接口的实现中需要注意的一些小细节,同时也对类、接口的多继承做了一些讲解。

先看一下继承

类的继承

子类继承了父类之后,就会将父类中定义的非 private 属性以及方法都继承下来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 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进来的属性方法创建出占位属性。 这告诉编译器这些成员在运行时是可用的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class SmartObject implements Person, Student {

    // Person
    name: string = 'person';
    sayHello: () => void;
    // Activatable
    grade: number = 3;
    study: () => void;
    }
  • 最后,把mixins混入定义的类,完成全部实现部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 把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];
    })
    });
    }

接口与类之间的继承

接口继承类的方式以及特点与上面类的继承和类的多继承一致。

接口与接口之间的多继承

接口与接口之间是可以直接进行多继承的。

  • 先定义两个接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 阿里接口
    interface Ali {
    pay: () => void
    }

    // 腾讯接口
    interface Tencent {
    game: string
    play: () => void
    }
  • 在定义一个接口继承 Ali、Tencent 接口

    1
    2
    3
    4
    5
    // 自己的接口
    interface Self extends Ali, Tencent {
    name: string
    say: () => void
    }

    此时Self 接口就包含了 Ali 和 Tencent 接口中所有的属性和方法

  • 验证一下
    用一个类继承 Self 接口,必须要将 Ali、 Tencent、 Self 接口中包含的所有属性和方法都声明了才可以,不然会编译报错

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 使用 Test 类实现 Self 接口
    class Test extends Self {
    game: string;
    name: string;
    pay() {
    cosole.log('经常用于支付');
    }

    play() => {
    cosole.log('可以玩各种游戏');
    }

    say() {
    cosole.log('不知道说点儿什么');
    }
    }

再来看一下接口的实现

接口的实现

接口在定义的时候,不能初始化属性以及方法,属性不能进行初始化,方法不能实现方法体。
类实现接口之后,必须声明接口中定义的属性以及方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();

类对于接口的多实现

一个类可以实现多个接口,不过要将实现的所有接口的属性和方法都实现了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 动物接口
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 方式模拟多继承的缺陷:

    • 只能在继承一级父类的方法和属性

    • 如果父类中含有同一种方法或属性,会根据赋值的顺序,先赋值的会被覆盖掉

  • 接口与接口之间可以直接进行多继承

  • 类实现接口可以进行多实现,每个接口用 , 隔开即可