概要
TypeScriptのstrictFunctionTypes
のコンパイラオプションについて調べていたら
Covariance, Contravariance, Bivarianceという概念が出てきた。よくわからないのでまとめてみる。
クラスの親子関係に基づく、関数パラメータの性質を定義するものである。
次の記事を大いに参考させていただいた。
今回の例
まずは次のようなクラスをセットアップする。
class Noun {} class City extends Noun {} class SanFrancisco extends City {}
このクラスに対応する関数をそれぞれの性質に基づいて定義する。
Invariance - 不変性
function method(value: Invariant<City>) {...} method(new Noun()); // error... method(new City()); // okay method(new SanFrancisco()); // error...
Invariance
(不変性) な関数は、子クラスも親クラスも受け付けない。
(該当クラスのみ受け付ける。)
Covariance - 共変性
function method(value: Covariant<City>) {...} method(new Noun()); // error... method(new City()); // okay method(new SanFrancisco()); // okay
Convariance
(共変性)な関数は 子クラスを受けつける。 親クラスは受け付けない。
(A->BならF(A)->F(B)という関係性)
Contravariance - 反変性
function method(value: Contravariant<City>) {...} method(new Noun()); // okay method(new City()); // okay method(new SanFrancisco()); // error...
Contravariance
(反変性)な関数は 親クラスを受けつける。 子クラスは受け付けない。
(A->BならF(B)->F(A)という関係性)
Bivariance - 双変性
function method(value: Bivariant<City>) {...} method(new Noun()); // okay method(new City()); // okay method(new SanFrancisco()); // okay
Bivariance
(双変性)な関数は、 親クラスも子クラスも受け付ける。
TypeScriptは...?
TypeScriptは関数について、引数は Bivariance
である。
ただし、strictFunctionTypes
というコンパイルオプションをtrueにすると、コンパイル時にVivariance
ではなくContravariance
でチェックしてくれるようになる。
(プログラミングの型のシステム上、関数のパラメータの性質はContravariance
の方が健全である。)