pco2699’s blog

学んだコード・技術について、保存しておく場所

Invariance、(不変性)、Covariance(共変性), Contravariance(反変性), Bivariance(双変性)について

概要

TypeScriptのstrictFunctionTypesのコンパイラオプションについて調べていたら
Covariance, Contravariance, Bivarianceという概念が出てきた。よくわからないのでまとめてみる。

クラスの親子関係に基づく、関数パラメータの性質を定義するものである。

次の記事を大いに参考させていただいた。

medium.com

今回の例

まずは次のようなクラスをセットアップする。

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の方が健全である。)