Distributive Conditional Types
Distributive Conditional Types は日本語では分配された条件型、条件付き型の分配、ユニオン分配、ユニオン型の分配法則などと呼ばれ、 Conditional Types が ジェネリクス型 に適用され、かつ型引数に ユニオン型 が与えられた場合に、そのユニオン型を構成する各メンバーに対して個別に条件判定が適用される(=分配される)性質を指します。
たとえば、次のような型があります。
ts
typeToArray <T > =T extends any ?T [] : never;
ts
typeToArray <T > =T extends any ?T [] : never;
このとき ToArray<number>
は number[]
, ToArray<string>
は string[]
となりますが、T
にユニオン型である number | string
を与えた場合、次のようになります。
ts
typeNumOrStrArray =ToArray <number | string>;
ts
typeNumOrStrArray =ToArray <number | string>;
これは次のような流れで型が解決されています。
ジェネリクス型に Conditional Types が適用され、ユニオン型(
number | string
) が与えられているため、分配の条件を満たす。tstypeToArray <T > =T extends any ?T [] : never;typeNumOrStrArray =ToArray <number | string>;tstypeToArray <T > =T extends any ?T [] : never;typeNumOrStrArray =ToArray <number | string>;ユニオンの個々の要素に対して
ToArray
が分配される。tstypeNumOrStrArray =ToArray <number> |ToArray <string>;tstypeNumOrStrArray =ToArray <number> |ToArray <string>;それぞれ
number[]
,string[]
となり、最終的な型としてnumber[] | string[]
を得る。tstypeNumOrStrArray = number[] | string[];tstypeNumOrStrArray = number[] | string[];
この性質は Conditional Types でジェネリクスが利用された時のみ起こります。たとえば次のような型エイリアスでは分配は行われません。
ts
typeToArray2 <T > =T [];typeNumOrStrArray2 =ToArray2 <number | string>;
ts
typeToArray2 <T > =T [];typeNumOrStrArray2 =ToArray2 <number | string>;
分配が起こるかどうかにより次のような違いが生まれるため、用途に応じて使い分けましょう。
ts
// 分配有りletnumOrStrArray :ToArray <number | string> = [1, 2, 3]; // OKnumOrStrArray = ["a", "b", "c"]; // OKType '(string | number)[]' is not assignable to type 'string[] | number[]'. Type '(string | number)[]' is not assignable to type 'string[]'. Type 'string | number' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'.2322Type '(string | number)[]' is not assignable to type 'string[] | number[]'. Type '(string | number)[]' is not assignable to type 'string[]'. Type 'string | number' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'.= [1, 2, "a"]; // NG numOrStrArray // 分配無しletnumOrStrArray2 :ToArray2 <number | string> = [1, 2, 3]; // OKnumOrStrArray2 = ["a", "b", "c"]; // OKnumOrStrArray2 = [1, 2, "a"]; // OK
ts
// 分配有りletnumOrStrArray :ToArray <number | string> = [1, 2, 3]; // OKnumOrStrArray = ["a", "b", "c"]; // OKType '(string | number)[]' is not assignable to type 'string[] | number[]'. Type '(string | number)[]' is not assignable to type 'string[]'. Type 'string | number' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'.2322Type '(string | number)[]' is not assignable to type 'string[] | number[]'. Type '(string | number)[]' is not assignable to type 'string[]'. Type 'string | number' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'.= [1, 2, "a"]; // NG numOrStrArray // 分配無しletnumOrStrArray2 :ToArray2 <number | string> = [1, 2, 3]; // OKnumOrStrArray2 = ["a", "b", "c"]; // OKnumOrStrArray2 = [1, 2, "a"]; // OK
分配を起こさせない方法
Conditional Types を利用したいが分配させたくないと言う場合、型変数を[]
で囲むことで分配を避けることができます。
ts
typeNotDistribute <T > = [T ] extends [string] ? true : false;
ts
typeNotDistribute <T > = [T ] extends [string] ? true : false;
このNotDistribute
型はstring
型に対してはtrue
を返しますが、string | number
型に対してはfalse
を返します。string | number
型はstring
型の部分型ではないため(つまり string | number extends string
は false のため)、 false
が返されます。
ts
typeA =NotDistribute <string>;typeB =NotDistribute <number>;typeC =NotDistribute <string | number>;
ts
typeA =NotDistribute <string>;typeB =NotDistribute <number>;typeC =NotDistribute <string | number>;