19
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TypeScriptのジェネリクスは共変なので気をつけて使おう

Last updated at Posted at 2022-10-04

コードを読めばクラスのジェネリクスは安心できないぜということがわかります。(念の為申し上げますが、ジェネリクスの使用を控えてと言っているわけではありません。ジェネリクスを使ってるクラスで型パラメータが違う場合は気をつけるべしということです。筆者は型パズルが好きです。)
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)
19
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?