Distributive Conditional Types
Distributive Conditional Types は日本語では分配された条件型、条件付き型の分配、ユニオン分配、ユニオン型の分配法則などと呼ばれ、 Conditional Types が ジェネリクス型 に適用され、かつ型引数に ユニオン型 が与えられた場合に、そのユニオン型を構成する各メンバーに対して個別に条件判定が適用される(=分配される)性質を指します。
たとえば、次のような型があります。
tstypeToArray <T > =T extends any ?T [] : never;
tstypeToArray <T > =T extends any ?T [] : never;
このとき ToArray<number> は number[], ToArray<string> は string[] となりますが、Tにユニオン型である number | string を与えた場合、次のようになります。
tstypeNumOrStrArray =ToArray <number | string>;
tstypeNumOrStrArray =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 でジェネリクスが利用された時のみ起こります。たとえば次のような型エイリアスでは分配は行われません。
tstypeToArray2 <T > =T [];typeNumOrStrArray2 =ToArray2 <number | string>;
tstypeToArray2 <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 を利用したいが分配させたくないと言う場合、型変数を[]で囲むことで分配を避けることができます。
tstypeNotDistribute <T > = [T ] extends [string] ? true : false;
tstypeNotDistribute <T > = [T ] extends [string] ? true : false;
このNotDistribute型はstring型に対してはtrueを返しますが、string | number型に対してはfalseを返します。string | number型はstring型の部分型ではないため(つまり string | number extends string は false のため)、 false が返されます。
tstypeA =NotDistribute <string>;typeB =NotDistribute <number>;typeC =NotDistribute <string | number>;
tstypeA =NotDistribute <string>;typeB =NotDistribute <number>;typeC =NotDistribute <string | number>;