抽象クラス(abstract class
)を使うと、あるプロパティ値の定義を必ず行ってほしいことを継承先のクラスへ伝えることができます。
抽象クラスは、単体では使うことができません。必ずクラスで継承して使います。
インターフェース(interface
)との違いは、抽象クラスは共通になりそうな部分はプロパティ値を入れたりやメソッドを実装した状態で用意できる点です。
抽象クラスを定義する
クラスはclass
というワードで定義していましたが、これはabstract class
というワードで定義します。また、サブクラスに実装を任せたい値やメソッドにもabstract
修飾子を付けられます。
abstract class A {
// あとで!
abstract foo: string;
// あとで!
abstract bar(): string;
baz() {
console.log('baz');
}
}
注意なのが、abstract
修飾子は、public
, protected
, private
のようなアクセス修飾子やプライベートフィールド(#
プレフィックスから始まるプロパティ)と一緒に使うことはできません。以下のような書き方は全部エラーになります。
abstract public foo: string;
abstract private foo: string;
abstract #foo: string
定義した抽象クラスを使うには、クラスの継承のようにextends
のあとに置くだけです。と言っても、以下のように書いただけの時AA
は型エラーになります。
class AA extends A {}
// Non-abstract class 'AA' does not implement inherited
// abstract member 'bar' from class 'A'.
//
// Non-abstract class 'AA' does not implement inherited
// abstract member 'foo' from class 'A'.
これはabstract
修飾子を付けて宣言したものは、継承先で必ず実装しなければならない為です。foo
とbar
を以下のように実装すれば型エラーは解消できます。
class AA extends A {
foo = 'foo';
bar() {
return 'bar';
}
}
注意なのが上記のfoo
はpublic foo
とはできても、protected foo
やprivate foo
とは宣言できません。abstract
はサブクラスではpublic
アクセス修飾子と同等になります。
インスタンスを作成した時に、baz
は抽象クラスで宣言したものがそのまま使うことができます。もちろんAA
で実装したfoo
とbar
も使えます。
const aa = new AA();
aa.baz();
// 'baz'
aa.foo;
aa.bar();
抽象クラスの継承
抽象クラスは以下のように継承することができます。
abstract class AA extends A {
foo = 'foo';
abstract qux: string;
}
foo
を抽象プロパティではなくしたり、新たな抽象プロパティ・メソッドを宣言することができます。
これを継承して作るクラスはbar
とqux
の実装が必須になります。
class AAA extends AA {
bar() {
return 'bar';
}
qux = 'qux';
}