[Typescript] クラスの継承と抽象クラスの使い方

[Typescript] クラスの継承と抽象クラスの使い方

Typescriptの継承

Typescript のクラスは別のクラスを継承することが可能です。

継承には クラス名を宣言した後ろに extends を使います。

class ParentClass {
    xxx: string;
    protected hoge: number = 1;

    parentMethod() {
        console.log('parent method called ...');
    }

    sampleMethod() {
        console.log('parent sample method called ...');
    }
}

class ChildClass extends ParentClass {
    yyy: string;

    childMethod() {
        // 親のメンバやメソッドを参照できる
        // protected なメンバやメソッドも継承したクラス内でなら触れる
        // this.xxx;
        // this.hoge;

        console.log('child method called ...');
    }

    // 継承した親と同じ名前のメソッドを定義すると上書きされる
    sampleMethod() {
        console.log('child sample method called ...');
    }
}


const child = new ChildClass();
child.xxx = "xxx";   // 親のメンバ
child.yyy = "yyy";
child.parentMethod(); // 親のメソッド
child.sampleMethod(); // 子の関数で上書き
// child.hoge <- これは protected なので呼べない

Playground

上記の例の通り、このクラスをインスタンス化すると親のメンバやメソッドにアクセスできます。アクセス修飾子を使えば、公開範囲を制限できます。

親のメンバやメソッドに protected をつけると、継承したクラス内からしか触れなくなります。上の例では、hoge は継承した child クラス内(もしくは親クラス内)からしかアクセスできません。

Typescriptの抽象クラス

抽象クラスはインスタンス化することができません。

上記例であれば ParentClass を直接 new でインスタンス化することができますが、抽象クラスはこれができなくなっています。継承しなければ意味がないようなクラスに対して使用します。

抽象クラスを定義するには abstract を付与します。

abstract class ParentClass {
    // 抽象クラス内に実装を持つメンバ
    xxx: string = "yyy";
    test(): void {
        /* ... */
    }

    // 継承したクラスで実装しなければならないメンバメンバ
    abstract yyy: string;
    abstract abstractTest(): void;
}

class ChildClass extends ParentClass {
    // 抽象メンバを実装しなければコンパイルエラーになる
    yyy: string;
    abstractTest() {

    }
}

// 抽象クラスはインスタンス化できない
// const parent = new ParentClass();

Playground

抽象クラスは必要なメンバの実装を強制します。イメージとしてはデフォルト実装付きのインターフェースみたいな感じです。

なお継承は1つのみで多重継承は不可になっています。複数クラスの継承はインターフェースで代用するのですが、実装の多重継承は不可能です。

Typescriptのインターフェース

インターフェースは実装するべきメンバ、メソッドを定義します。

implements をつけることで、class にインターフェースを実装させる事ができます。

インターフェースは実装したクラスのインスタンスでなくても、実装するべきメンバを満たしていれば、リテラルオブジェクトでも関係ないクラスのインスタンスでも同じように扱えます。

interface Person {
    name: string;
    age?: number; // ?をつけると未定義可能になる
    hello(): void;
}

class Taro implements Person {
    name: string = "tarou";
    hello(): void {
        console.log('hello ...');
    }
}

const taro = new Taro();
taro.hello();

const jiro: Person = {
    name: 'jiro',
    age: 20,
    hello: () => console.log('hello ...')
}

Playground

複数のインターフェースを実装し、同時にクラスを継承する

extendsimplements は同時に使用することが可能です。またクラスの多重継承はできませんが、インターフェースを複数指定することは可能です。

以下、複数のインターフェースを実装し、抽象クラスを継承する例です。

abstract class A { abstract a: string; }
interface X { x: number }
interface Y { y: number }

// A を継承し、X, Y を実装するクラス
class Hoge extends A implements X, Y {
    a = "a";
    x = 1;
    y = 2;
}

const a: A = new Hoge();
const x: X = new Hoge();
const y: Y = new Hoge();

Playground

extendsimplements は この順でないといけません。継承するクラスをカンマ区切りで複数指定してもコンパイルエラーになります。

まとめ

  • 継承は extends
  • インターフェースの実装は implements
  • 複数のインターフェースを実装するときはカンマ区切りで指定

以上。

Typescriptカテゴリの最新記事