コードを読めばクラスのジェネリクスは安心できないぜということがわかります。(念の為申し上げますが、ジェネリクスの使用を控えてと言っているわけではありません。ジェネリクスを使ってるクラスで型パラメータが違う場合は気をつけるべしということです。筆者は型パズルが好きです。)
JavaのジェネリクスとTypeScriptのジェネリクスは仕様が違います。
Javaのジェネリクスは不変。TypeScriptのクラスのジェネリクスは共変です。インターフェースに関してはTypeScriptは個々のフィールドか代入可能かどうかちゃんと判断してるっぽいです。そのうちコード追加できたらやります。
// ただのスーパークラス
class SuperClass{
public name: string = "super"
}
// サブクラスその1
class SubClass1 extends SuperClass{
public email = "bar";
}
// サブクラスその2
class SubClass2 extends SuperClass{
public address = "baz";
}
// ジェネリクスを使った配列のラッパークラスのようなもの
// 今回の主役
class List<T>{
public array: T[] = [];
public push(item: T) {
this.array.push(item);
}
public get(index: number) {
return this.array[index];
}
}
// Listをインスタンス化
let sub1List = new List<SubClass1>();
let superList = new List<SuperClass>();
// superListにsub1Listの参照を渡す(TypeScriptのジェネリクスは共変なので可能)
superList = sub1List;
// SubClass2はSuperClassのサブクラスなので以下のコードはコンパイルエラーにならないが、、、
// 実際はsuperListはsub1Listを参照しているのでsub1ListにSubClass2のインスタンスが追加されている
superList.push(new SubClass2());
// itemの方はSubClass1と判定されているが実際はSubClass2である。
const item: SubClass1 = sub1List.get(0);
// したがって、したのコードの出力は
// undefined baz
// となる。
console.log(item.email,(item as SuperClass as SubClass2).address)