メインコンテンツへスキップ

型変性

型変性は、型システムで頻繁に話題になるトピックです。これは、部分型付けに関して型パラメータがどのように振る舞うかを決定するために使用されます。

まず、互いに拡張するいくつかのクラスを設定します。

class Noun {}
class City extends Noun {}
class SanFrancisco extends City {}

ジェネリック型 のセクションで見たように、型パラメータが出力位置で使用される場合、入力位置で使用される場合、またはどちらの位置で使用される場合を記述するために、変性記号を使用できます。

ここでは、これらの各ケースを詳しく見ていきます。

共変性

例えば、次の型を考えてみましょう。

type CovariantOf<X> = {
+prop: X;
getter(): X;
}

ここで、`X` は厳密に出力位置に現れます。これは、`CovariantOf<X>`型のオブジェクト`o`から、プロパティアクセス`o.prop`または`o.getter()`の呼び出しを通じて情報を読み取るために使用されます。

`prop`は読み取り専用のプロパティであるため、オブジェクト`o`への参照を通じてデータを投入する方法はありません。

これらの条件が満たされる場合、`CovariantOf`の定義で`X`に記号`+`を使用して注釈を付けることができます。

type CovariantOf<+X> = {
+prop: X;
getter(): X;
}

これらの条件は、部分型付けに関して`CovariantOf<T>`型のオブジェクトを扱う方法に重要な影響を与えます。念のため、部分型付けルールは「`T`型の値を期待するコンテキストがある場合、`S`型の値を渡すのは安全か?」という質問に答えるのに役立ちます。これが当てはまる場合、`S`は`T`の部分型です。

`CovariantOf`の定義を使用し、`City`が`Noun`の部分型であるとすると、`CovariantOf<City>`も`CovariantOf<Noun>`の部分型であることも事実です。実際

  • `Noun`型のプロパティが期待されている場合に、`City`型のプロパティ`prop`を読み取るのは安全であり、
  • `Noun`型の値が期待されている場合、`getter()`を呼び出すときに`City`型の値を返すのは安全です。

これら2つを組み合わせると、`CovariantOf<Noun>`が期待される場合は常に、`CovariantOf<City>`を使用するのが安全です。

共変性が使用される一般的な例は$ReadOnlyArray<T>です。`prop`プロパティと同様に、`$ReadOnlyArray`参照を使用して配列にデータ書き込むことはできません。これにより、より柔軟な部分型付けルールが可能になります。Flowは、`$ReadOnlyArray<S>`が`$ReadOnlyArray<T>`の部分型であると判断するために、`S`が`T`の部分型であることを証明するだけで済みます。

不変性

`X`の使用に関する制限を緩和して、例えば`prop`を読み書き可能なプロパティにしようとするとどうなるか見てみましょう。次の型定義になります。

type NonCovariantOf<X> = {
prop: X;
getter(): X;
};

`NonCovariantOf<City>`型の変数`nonCovariantCity`も宣言しましょう。

declare const nonCovariantCity: NonCovariantOf<City>;

ここで、`nonCovariantCity`を`NonCovariantOf<Noun>`型のオブジェクトと見なすのは安全ではありません。これが許可された場合、次の宣言が可能になります。

const nonCovariantNoun: NonCovariantOf<Noun> = nonCovariantCity;

この型は、次の代入を許可します。

nonCovariantNoun.prop = new Noun;

これは、`prop`フィールドに`Noun`が格納されるようになるため、`nonCovariantCity`の元の型が無効になります。

`NonCovariantOf`と`CovariantOf`の定義を区別するのは、型パラメータ`X`が入出力の両方の位置で使用されていることです。これは、プロパティ`prop`への読み書きの両方で使用されているためです。このような型パラメータは不変と呼ばれ、変性のデフォルトケースであり、プレフィックス記号は必要ありません。

type InvariantOf<X> = {
prop: X;
getter(): X;
setter(X): void;
};

変数を想定すると

declare const invariantCity: InvariantOf<City>;

は、

  • `InvariantOf<Noun>`が必要なコンテキストでは使用できません。`Noun`をプロパティ`prop`に書き込むことはできないためです。
  • `InvariantOf<SanFrancisco>`が必要なコンテキストでは使用できません。`prop`の読み取りによって返される`City`が`SanFrancisco`でない可能性があるためです。

つまり、`InvariantOf<City>`は`InvariantOf<Noun>`の部分型でも`InvariantOf<SanFrancisco>`の部分型でもありません。

反変性

型パラメータが入力位置でのみ使用される場合、それは反変の方法で使用されていると言います。これは、構造体にデータ書き込む位置でのみ表示されることを意味します。この種の型パラメータを記述するには、記号`-`を使用します。

type ContravariantOf<-X> = {
-prop: X;
setter(X): void;
};

一般的な反変の位置には、書き込み専用のプロパティと「setter」関数があります。

`ContravariantOf<City>`型のオブジェクトは、`ContravariantOf<SanFrancisco>`型のオブジェクトが期待される場合はいつでも使用できますが、`ContravariantOf<Noun>`が期待される場合は使用できません。言い換えれば、`ContravariantOf<City>`は`ContravariantOf<SanFrancisco>`の部分型ですが、`ContravariantOf<Noun>`の部分型ではありません。これは、任意の`City`を書き込むことができるプロパティに`SanFrancisco`を書き込むのは問題ありませんが、任意の`Noun`を書き込むのは安全ではないためです。