TypeScriptのclass
は、2つの役割がある。
- コンストラクタのシグネチャを定義をする
- クラスのインスタンスの型をインターフェイスとして定義をする
class
とinterface
の線引きが比較的明瞭な言語(JavaやPHPなど)からすると、前者はすんなり理解できるものだが、後者には意外性があるかもしれない。「class
を書いたら、interface
も書いたことになる」ということだからだ。
TypeScriptのclass
定義の振る舞いをひとつひとつ確認していこう。
ここに、2つの属性を持ったRect
クラスの定義がある。
class Rect {
x: number = 0
y: number = 0
}
new クラス名
がコンパイルに通るのは、1つ目の「コンストラクタのシグネチャを定義をする」役割によるものである。これは、クラスを使ったことがある経験があれば、何の不思議もないだろう。
const rect = new Rect()
2つ目の役割である「クラスのインスタンスの型をインターフェイスとして定義をする」ことは、TypeScriptの興味深い仕様だ。先のclass Rect
のコードの裏で、次のような型定義がなされている:
interface Rect {
x: number
y: number
}
型アノテーションでRect
が使えるのは、そのためだ。次のコードは、new Rect()
の戻り値がRect
型であることを表現した一文であるが、これ自体は別段不思議はないだろう:
const rect: Rect = new Rect()
^^^^
これと似たようなコードではあるが、次のサンプルコードはTypeScriptらしさがより強く分かる。
const rect: Rect = { x: 3, y: 3 }
^^^^^^^^^^^^^^
rect
は型アノテーションでRect
型であることを宣言しているが、その値はRect
クラスではない。あくまで、Rect
インターフェイスを実装したオブジェクトである。このコードはコンパイルがちゃんと通る。
ここまでで、Rect
がインターフェイスであることが分かったので、もっとインターフェイスさがよく分かる例を見ていこう。
Rect
はインターフェイスなので、普通のinterface
と同様に、クラスでimplements
することも当然のようにできる。
class Cube implements Rect {
^^^^^^^^^^
x: number = 0
y: number = 0
z: number = 0
}
const rect: Rect = new Cube()
extends
と違うところは、Cube
がRect
を継承しているわけではないというところだ。
Rect
はインターフェイスなので、普通のinterface
の親にすることができる:
interface Cube extends Rect {
^^^^^^^^^ ^^^^^^^
z: number
}
const cube: Cube = { x: 1, y: 2, z: 3 }
const rect: Rect = cube
Rect
はインターフェイスなので、普通のinterface
同様にopen-endedの特徴も持っている。したがって、class
を定義とは別に、クラスと同名のinterface
を定義することで、Declaration Mergingを発生させることもできる。
class Rect {
x: number = 0
y: number = 0
}
// 追加のインターフェイス定義
interface Rect {
area(): number
}
上のコードでは、Rect
に追加でarea()
メソッドを定義している。
もちろん、これだけではarea()
メソッドの実装が存在していないので、Rect
クラスをnew
しただけの次のようなコードでは、コンパイルは通るものの当然ながらランタイムエラーになる。
const rect = new Rect()
rect.area() // runtime error: Uncaught TypeError: rect.area is not a function
所感
TypeScriptのclass = インスタンスのインターフェイス定義 + コンストラクタの定義
— suin❄️Terraformエンジニア募集中 (固定ツイ参照) (@suin) November 4, 2019
「classを書くと、暗黙的にinterfaceも定義している」ということになる。
これは、他の言語からすると「意外さ」でもあると同時に、TypeScriptの柔軟さを高める要素でもあるような気がする。 pic.twitter.com/kMSNZWCLmy