変性 (variance)
TypeScriptでは、型の互換性を判定する際に変性(variance)という概念が使われます。変性とは型同士の関係性を示すもので、TypeScriptにおいてはこの変性を示すためにはジェネリクスの型変数の前にin
あるいはout
を付与します。
なお、ここで語られる変性はtsconfig.jsonのstrictFunctionTypes
の設定でも変更することができます。
今回は特筆しない限りstrictFunctionTypes
はfalse
として説明します。
📄️ strictFunctionTypes
引数型の変性のチェックを厳しくする
共変性 (Covariance)
共変性はジェネリクスの型変数にout
を付与した場合の変位です。共変性とはサブタイプの関係が保たれることを意味します。
反変性 (Contravariance)
反変性はジェネリクスの型変数にin
を付与した場合の変位です。反変性とはサブタイプの関係が逆転することを意味します。
不変性 (Invariance)
不変性はジェネリクスの型変数にin
とout
を付与した場合の変位です。不変性とは型が共変性でも反変性でもないことを意味します。
双変性 (Bivariance)
双変性はジェネリクスの型変数にin
とout
を付与しない場合の変位です。
ここで例としてひとつの引数I
を受け取り戻り値O
を返す関数の型としてBivariantFunction<I, O>
(変位をつけていないのでTypeScriptとしては双変と同じ)を定義します。
ts
typeBivariantFunction <I ,O > = (arg :I ) =>O ;
ts
typeBivariantFunction <I ,O > = (arg :I ) =>O ;
ここで引数I
を共変にしたCovariantFunction<in I, O>
と戻り値O
を反変にしたContravariantFunction<I, out O>
、引数も戻り値も不変にしたInvariantFunction<in out I, in out O>
を定義します。するとそれらは次のように定義されます。
ts
typeBivariantFunction <I ,O > = (arg :I ) =>O ;typeCovariantFunction <I , outO > =BivariantFunction <I ,O >;typeContravariantFunction <inI ,O > =BivariantFunction <I ,O >;typeInvariantFunction <in outI , in outO > =BivariantFunction <I ,O >;
ts
typeBivariantFunction <I ,O > = (arg :I ) =>O ;typeCovariantFunction <I , outO > =BivariantFunction <I ,O >;typeContravariantFunction <inI ,O > =BivariantFunction <I ,O >;typeInvariantFunction <in outI , in outO > =BivariantFunction <I ,O >;
クラスの継承関係を使った例
継承関係がわかりやすくなるようにクラスA
, B
, C
を定義します。A
はB
を継承し、B
はC
を継承しており、メソッドを追加しました。
ts
classA {publica (): void {console .log ("a");}}classB extendsA {publicb (): void {console .log ("b");}}classC extendsB {publicc (): void {console .log ("c");}}
ts
classA {publica (): void {console .log ("a");}}classB extendsA {publicb (): void {console .log ("b");}}classC extendsB {publicc (): void {console .log ("c");}}
各変性の関数のI
とO
の両方をB
にした関数を定義します。
ts
declare constbiFunc :BivariantFunction <B ,B >;declare constcoFunc :CovariantFunction <B ,B >;declare constcontraFunc :ContravariantFunction <B ,B >;declare constinFunc :InvariantFunction <B ,B >;
ts
declare constbiFunc :BivariantFunction <B ,B >;declare constcoFunc :CovariantFunction <B ,B >;declare constcontraFunc :ContravariantFunction <B ,B >;declare constinFunc :InvariantFunction <B ,B >;
これらの関数のジェネリクスを変更してみます。
ts
constf01 :BivariantFunction <A ,B > =biFunc ;constf02 :BivariantFunction <C ,B > =biFunc ;constf03 :BivariantFunction <B ,A > =biFunc ;constType 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.2322Type 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.: f04 BivariantFunction <B ,C > =biFunc ;constf05 :CovariantFunction <B ,A > =coFunc ;constType 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, C>'. Type 'B' is not assignable to type 'C'.2322Type 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, C>'. Type 'B' is not assignable to type 'C'.: f06 CovariantFunction <B ,C > =coFunc ;constType 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.2322Type 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.: f07 ContravariantFunction <A ,B > =contraFunc ;constf08 :ContravariantFunction <C ,B > =contraFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<A, B>'. Type 'A' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<A, B>'. Type 'A' is not assignable to type 'B'.: f09 InvariantFunction <A ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'B' is not assignable to type 'C'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'B' is not assignable to type 'C'.: f10 InvariantFunction <C ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, A>'. Type 'A' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, A>'. Type 'A' is not assignable to type 'B'.: f11 InvariantFunction <B ,A > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'B' is not assignable to type 'C'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'B' is not assignable to type 'C'.: f12 InvariantFunction <B ,C > =inFunc ;
ts
constf01 :BivariantFunction <A ,B > =biFunc ;constf02 :BivariantFunction <C ,B > =biFunc ;constf03 :BivariantFunction <B ,A > =biFunc ;constType 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.2322Type 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.: f04 BivariantFunction <B ,C > =biFunc ;constf05 :CovariantFunction <B ,A > =coFunc ;constType 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, C>'. Type 'B' is not assignable to type 'C'.2322Type 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, C>'. Type 'B' is not assignable to type 'C'.: f06 CovariantFunction <B ,C > =coFunc ;constType 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.2322Type 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.: f07 ContravariantFunction <A ,B > =contraFunc ;constf08 :ContravariantFunction <C ,B > =contraFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<A, B>'. Type 'A' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<A, B>'. Type 'A' is not assignable to type 'B'.: f09 InvariantFunction <A ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'B' is not assignable to type 'C'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'B' is not assignable to type 'C'.: f10 InvariantFunction <C ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, A>'. Type 'A' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, A>'. Type 'A' is not assignable to type 'B'.: f11 InvariantFunction <B ,A > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'B' is not assignable to type 'C'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'B' is not assignable to type 'C'.: f12 InvariantFunction <B ,C > =inFunc ;
これらの中でエラーになるものをまとめると
f04
は引数、戻り値ともに双変でスーパータイプとサブタイプの関係が許容されますが、戻り値のC
に対しB
はメソッドc()
を持っていないためエラーになりますf06
は戻り値が共変でスーパータイプの割り当てが許容されますが、戻り値C
はB
のサブタイプなのでエラーになりますf07
は引数が反変でサブタイプの割り当てが許容されますが、引数A
はB
のスーパータイプなのでエラーになりますf09
は引数、戻り値ともに不変でスーパータイプとサブタイプの関係が許容されないためエラーになりますf10
は引数、戻り値ともに不変でスーパータイプとサブタイプの関係が許容されないためエラーになりますf11
は引数、戻り値ともに不変でスーパータイプとサブタイプの関係が許容されないためエラーになりますf12
は引数、戻り値ともに不変でスーパータイプとサブタイプの関係が許容されないためエラーになります
また、strictFunctionTypes
をtrue
にすると上記のエラーに加えて
f01
は引数、戻り値ともに双変でスーパータイプとサブタイプの関係が許容されますが、引数のA
はメソッドb()
を持っていないためエラーになります
ユニオン型を使った例
ユニオン型を使って継承関係を表してみます。
ts
typeA = null;typeB = null | undefined;typeC = null | undefined | string;
ts
typeA = null;typeB = null | undefined;typeC = null | undefined | string;
このときA
はB
の部分型であり、B
はC
の部分型です。言い換えるとA extends B
、B extends C
です。
ts
constf01 :BivariantFunction <A ,B > =biFunc ;constf02 :BivariantFunction <C ,B > =biFunc ;constType 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f03 BivariantFunction <B ,A > =biFunc ;constf04 :BivariantFunction <B ,C > =biFunc ;constType 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f05 CovariantFunction <B ,A > =coFunc ;constf06 :CovariantFunction <B ,C > =coFunc ;constf07 :ContravariantFunction <A ,B > =contraFunc ;constType 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.2322Type 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.: f08 ContravariantFunction <C ,B > =contraFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<null, B>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<null, B>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f09 InvariantFunction <A ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'C' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'C' is not assignable to type 'B'.: f10 InvariantFunction <C ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f11 InvariantFunction <B ,A > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'C' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'C' is not assignable to type 'B'.: f12 InvariantFunction <B ,C > =inFunc ;
ts
constf01 :BivariantFunction <A ,B > =biFunc ;constf02 :BivariantFunction <C ,B > =biFunc ;constType 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f03 BivariantFunction <B ,A > =biFunc ;constf04 :BivariantFunction <B ,C > =biFunc ;constType 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f05 CovariantFunction <B ,A > =coFunc ;constf06 :CovariantFunction <B ,C > =coFunc ;constf07 :ContravariantFunction <A ,B > =contraFunc ;constType 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.2322Type 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.: f08 ContravariantFunction <C ,B > =contraFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<null, B>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<null, B>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f09 InvariantFunction <A ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'C' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'C' is not assignable to type 'B'.: f10 InvariantFunction <C ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f11 InvariantFunction <B ,A > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'C' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'C' is not assignable to type 'B'.: f12 InvariantFunction <B ,C > =inFunc ;
f03
は引数、戻り値ともに双変でスーパータイプとサブタイプの関係が許容されますが、戻り値のA
に対しB
のundefined
はnull
に割り当てることができないためエラーになりますf05
は戻り値が共変でスーパータイプの割り当てが許容されますが、戻り値A
に対しB
のundefined
はnull
に割り当てることができないためエラーになりますf08
は引数が反変でサブタイプの割り当てが許容されますが、引数C
のstring
はB
に割り当てることができないためエラーになりますf09
は引数、戻り値ともに不変でスーパータイプとサブタイプの関係が許容されないためエラーになりますf10
は引数、戻り値ともに不変でスーパータイプとサブタイプの関係が許容されないためエラーになりますf11
は引数、戻り値ともに不変でスーパータイプとサブタイプの関係が許容されないためエラーになりますf12
は引数、戻り値ともに不変でスーパータイプとサブタイプの関係が許容されないためエラーになります
strictFunctionTypes
をtrue
にすると上記のエラーに加えて
f02
は引数、戻り値ともに双変でスーパータイプとサブタイプの関係が許容されますが、引数C
のstring
はB
に割り当てることができないためエラーになります
継承関係を見る
継承関係を見るためにConditional Typesを使ってみましょう。ある型T
がU
のサブタイプかどうかを判定する型IsSubType<T, U>
を定義します。
ts
typeIsSubType <T ,U > =T extendsU ? true : false;
ts
typeIsSubType <T ,U > =T extendsU ? true : false;
先ほどのクラスA
, B
, C
にIsSubType<T, U>
を適用してみます。
ts
classA {publica (): void {console .log ("a");}}classB extendsA {publicb (): void {console .log ("b");}}classC extendsB {publicc (): void {console .log ("c");}}declare constt1 :IsSubType <A ,A >;declare constt2 :IsSubType <B ,B >;declare constt3 :IsSubType <C ,C >;declare constt4 :IsSubType <A ,B >;declare constt5 :IsSubType <B ,C >;declare constt6 :IsSubType <A ,C >;declare constt7 :IsSubType <B ,A >;declare constt8 :IsSubType <C ,B >;declare constt9 :IsSubType <C ,A >;
ts
classA {publica (): void {console .log ("a");}}classB extendsA {publicb (): void {console .log ("b");}}classC extendsB {publicc (): void {console .log ("c");}}declare constt1 :IsSubType <A ,A >;declare constt2 :IsSubType <B ,B >;declare constt3 :IsSubType <C ,C >;declare constt4 :IsSubType <A ,B >;declare constt5 :IsSubType <B ,C >;declare constt6 :IsSubType <A ,C >;declare constt7 :IsSubType <B ,A >;declare constt8 :IsSubType <C ,B >;declare constt9 :IsSubType <C ,A >;
自分自身のクラスあるいはサブクラスに限りtrue
が返されることがわかります。
ユニオン型も見てみましょう。なお、こちらはユニオン型であるためDistributive Conditional Typeを使用します。
📄️ ユニオン分配
ユニオン分配はジェネリクスで使われる型変数Tに対しユニオン型が指定された場合、その各要素に対してジェネリクスの型変数を適用することを指します。
ts
typeIsSubType <T ,U > = [T ] extends [U ] ? true : false;
ts
typeIsSubType <T ,U > = [T ] extends [U ] ? true : false;
ts
typeA = null;typeB = null | undefined;typeC = null | undefined | string;declare constt1 :IsSubType <A ,A >;declare constt2 :IsSubType <B ,B >;declare constt3 :IsSubType <C ,C >;declare constt4 :IsSubType <A ,B >;declare constt5 :IsSubType <B ,C >;declare constt6 :IsSubType <A ,C >;declare constt7 :IsSubType <B ,A >;declare constt8 :IsSubType <C ,B >;declare constt9 :IsSubType <C ,A >;
ts
typeA = null;typeB = null | undefined;typeC = null | undefined | string;declare constt1 :IsSubType <A ,A >;declare constt2 :IsSubType <B ,B >;declare constt3 :IsSubType <C ,C >;declare constt4 :IsSubType <A ,B >;declare constt5 :IsSubType <B ,C >;declare constt6 :IsSubType <A ,C >;declare constt7 :IsSubType <B ,A >;declare constt8 :IsSubType <C ,B >;declare constt9 :IsSubType <C ,A >;
A
はB
の部分型であり、B
はC
の部分型であるためt4
、t5
、t6
がtrue
になることがわかります。