TypeScriptの型判定でよくわかってないところがあったのでまとめます。
今回は構造的部分型と公称型についてなのですが、自分が慣れてたのは公称型の考えだったようでTypeScriptで間違いまくってました。
サバイバルTypeScript - 構造的部分型 (structural subtyping)
サバイバルTypeScript - 公称型
関数の引数で考えるとわかりやすそうなのですが、それぞれ次のように関数fが引数としてクラスAとBのインスタンスを受ける場合に型エラーになる場合とならない場合が出ます。
- 構造的部分型
class A {
p = 0;
num: number;
constructor () {
this.num = 10;
}
}
class B {
p = 0;
num: number;
constructor () {
this.num = 20;
}
}
const f = (a: A) => {
console.log(a);
};
const a = new A();
f(a); // OK
const b = new B();
f(b); // OK
- 公称型
class A {
private p = 0;
num: number;
constructor () {
this.num = 10;
}
}
class B {
private p = 0;
num: number;
constructor () {
this.num = 20;
}
}
const f = (a: A) => {
console.log(a);
};
const a = new A();
f(a); // OK
const b = new B();
f(b); // NG
公称型の場合はprivateプロパティが一致していても関数fにクラスBのインスタンスを渡すことができません。
気をつけてこの辺を扱わないとどうしてもエラーが解消できずに時間を費やすことになりそうです。
逆に意図的に公称型としてクラスを定義したいときは、強制的にprivateもしくはprotectedのプロパティやメソッドを持てばいいことになります。
ただそのためだけに定義されたプロパティなどに意味をもたせるのは難しいのでコメントなどで補足するか、コーディングルールにするとかしておいた方が混乱しないかもしれません。