Distributive Conditional Types
Distributive Conditional Types は日本語では分配された条件型、条件付き型の分配、ユニオン分配、ユニオン型の分配法則などと呼ばれ、 Conditional Types が ジェネリクス型 に適用され、かつ型引数に ユニオン型 が与えられた場合に、そのユニオン型を構成する各メンバーに対して個別に条件判定が適用される(=分配される)性質を指します。
たとえば、次のような型があります。
tsToArray <T > =T extends any ?T [] : never;
tsToArray <T > =T extends any ?T [] : never;
このとき ToArray<number> は number[], ToArray<string> は string[] となりますが、Tにユニオン型である number | string を与えた場合、次のようになります。
tsNumOrStrArray =ToArray <number | string>;
tsNumOrStrArray =ToArray <number | string>;
これは次のような流れで型が解決されています。
- ジェネリクス型に Conditional Types が適用され、ユニオン型( - number | string) が与えられているため、分配の条件を満たす。tstype- ToArray <- T > =- T extends any ?- T [] : never;type- NumOrStrArray =- ToArray <number | string>;tstype- ToArray <- T > =- T extends any ?- T [] : never;type- NumOrStrArray =- ToArray <number | string>;
- ユニオンの個々の要素に対して - ToArrayが分配される。tstype- NumOrStrArray =- ToArray <number> |- ToArray <string>;tstype- NumOrStrArray =- ToArray <number> |- ToArray <string>;
- それぞれ - number[],- string[]となり、最終的な型として- number[] | string[]を得る。tstype- NumOrStrArray = number[] | string[];tstype- NumOrStrArray = number[] | string[];
この性質は Conditional Types でジェネリクスが利用された時のみ起こります。たとえば次のような型エイリアスでは分配は行われません。
tsToArray2 <T > =T [];typeNumOrStrArray2 =ToArray2 <number | string>;
tsToArray2 <T > =T [];typeNumOrStrArray2 =ToArray2 <number | string>;
分配が起こるかどうかにより次のような違いが生まれるため、用途に応じて使い分けましょう。
tsnumOrStrArray :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
tsnumOrStrArray :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 を利用したいが分配させたくないと言う場合、型変数を[]で囲むことで分配を避けることができます。
tsNotDistribute <T > = [T ] extends [string] ? true : false;
tsNotDistribute <T > = [T ] extends [string] ? true : false;
このNotDistribute型はstring型に対してはtrueを返しますが、string | number型に対してはfalseを返します。string | number型はstring型の部分型ではないため(つまり string | number extends string は false のため)、 false が返されます。
tsA =NotDistribute <string>;typeB =NotDistribute <number>;typeC =NotDistribute <string | number>;
tsA =NotDistribute <string>;typeB =NotDistribute <number>;typeC =NotDistribute <string | number>;