分散(variance) は、複合型の部分型関係が、その構成要素の部分型関係とどう関係するかを表します。つまり、Container<Sub> が Container<Super> に代入可能なのはどんなときか、ということです。
Dog を Animal の部分型とします。
共変性(covariance) — 方向を保つ(直感的なケース)
ts
: [] = [];
: [] = dogs;
分散(variance) は、複合型の部分型関係が、その構成要素の部分型関係とどう関係するかを表します。つまり、Container<Sub> が Container<Super> に代入可能なのはどんなときか、ということです。
Dog を Animal の部分型とします。
: [] = [];
: [] = dogs;
ジュニアからシニアまで、詳細な回答付きのIT面接質問ライブラリ。
寄付する戻り値の型と配列は 共変 です。Dog ⊆ Animal なら Dog[] ⊆ Animal[] となります。Dog を返す関数は、Animal を返す関数が期待される場所で使えます。
type Handler<T> = (arg: T) => void;
let animalHandler: Handler<Animal> = (a) => {};
let dogHandler: Handler<Dog> = animalHandler; // ✅ (with strictFunctionTypes)
// a handler that accepts ANY Animal can safely handle a Dog
関数の 引数 は反変です。Handler<Animal> は Handler<Dog> に代入可能で、これは要素の関係とは 逆 です。これは健全です。あらゆる動物を扱えるものは、当然ながら犬も扱えるからです。
// Method parameters in TS are bivariant by default (a known unsound convenience)
interface Comparer<T> { compare(a: T): void; }
TypeScript は、独立した関数型を反変的にチェックするのは strictFunctionTypes の下だけです。メソッドの引数は利便性のために意図的に 双変(bivariant) であり、これは技術的には健全ではありません。
分散は、特定の代入がなぜ許可される、あるいは拒否されるのかを説明します。なぜ Dog[] が Animal[] に収まるのに、(d: Dog) => void というコールバックが常に (a: Animal) => void の代わりになれるとは限らないのか、ということです。
これを理解することは、ジェネリックな API を設計する(例えば読み取り専用 vs 書き込み位置)際や、関数やジェネリックが絡む紛らわしい「代入できません」というエラーを読み解く際に役立ちます。