[[条件付き型 (TypeScript)|条件付き型]]で[[型パラメーター (TypeScript)|型パラメーター]]に[[ユニオン型 (TypeScript)|ユニオン型]]が[[型引数 (TypeScript)|型引数]]として指定されたとき、[[条件付き型 (TypeScript)|条件付き型]]を使わない[[型エイリアス (TypeScript)|型エイリアス]]とは結果が異なり、分配的(Distributive)になる。
具体的には以下2ステップで評価される。
1. [[ユニオン型 (TypeScript)|ユニオン型]]の1つ1つに対して型を算出する
2. 1の結果をすべてマージした[[ユニオン型 (TypeScript)|ユニオン型]]を結果とする
```ts
// 普通にtypeキーワードを使うと分配的にならない
type ThreeTuple<T> = [T, T, T];
type A = ThreeTuple<string | number>;
// ^? type A = [string | number, string | number, string | number]
// Conditionaly Typesを使うと分配的になる
type CThreeTuple<T> = T extends any ? [T, T, T] : never;
type B = CThreeTuple<string | number>;
// ^? type B = [string, string, string] | [number, number, number]
// Conditionaly Typesを使いつつ分配的を割けるなら[]で囲む
type WorkaroundCThreeTuple<T> = [T] extends [any] ? [T, T, T] : never;
type C = WorkaroundCThreeTuple<string | number>;
// ^? type C = [string | number, string | number, string | number]
```
<button class="playground"><a href="https://www.typescriptlang.org/play?#code/PTAEjszQsBMawYBcE8AOBTQtQyB+GQ9wzsJMMgkhkH95QMQZALBkDAlQWUTAQt2kCsGQSIZbARBgCgEVQAVACwCdlknAK6IANsgA8nAHygAvKADanADRc1nALoBuNkmSgAgvK58BwsZIDOsXgEsAdgHNQAH1AOhAWwBGyXtK6IKAhAHoA-CwswQDCAPYOACZ2sHYJAIai8Fz6VoSklDS0gNEMehwxPPyCIuJSsgqcoMgAHrDISVag6Q7Z4Uqq6lyaoABcHsgAbv667AYAQiYVZtWWEjb2zm4e3n4BQWBhkdFg8UkpaQ6Z2Zy5hEyAJgz3hXiATkqAigzFDIqagOYMgE9qgACGMoGADqcV4AGt0rw4kIkksqhZajITMphi02h0lN14MM+soNBphmMHJNpsDQDETGDIdDYfDKuYatZbI4XO5PL5-IFjiFQBEWEA">Playground</a></button>
## Excludeによる具体的な説明
上記のサンプルコードだけではピンと来ないと思うので、より具体的な例で詳しく説明する。
`Exclude`で[[リテラル型 (TypeScript)|リテラル型]]の[[ユニオン型 (TypeScript)|ユニオン型]]から特定の[[リテラル型 (TypeScript)|リテラル型]]を除外するケースを考える。`Exclude`の実装は以下のようになっている。
```ts
type Exclude<T, U> = T extends U ? never : T;
```
ここで以下の`SafeColor`型を定義する。
```ts
type Color = "red" | "blue" | "green" | "yellow";
type DangerColor = "red" | "yellow";
type SafeColor = Exclude<Color, DangerColor>;
// ^? type SafeColor = "blue" | "green"
```
<button class="playground"><a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAwg9gGzgJygXigImRAJpqAHywCMEBXCA4zAcxwgDtqsQIEkB3TAbgChQkKABEAho1oRk8JKgzY8LTGw5xu-QdADKogGYQZKdFACiADwDGFXBAA8h5ABoR4ydMQoAfPwD0PqAEAegD8fEA">Playground</a></button>
`Exclude`の定義を当てはめてみる。
```ts
type Exclude<Color, DangerColor> =
Color extends DangerColor ? never : Color;
```
右辺を展開すると
```ts
"red" | "blue" | "green" | "yellow" extends "red" | "yellow" ? never : "red" | "blue" | "green" | "yellow";
```
ソースの通りに解釈してしまうと、『`"red" | "blue" | "green" | "yellow"`は`"red" | "yellow"`に代入できない』ので、この結果は `"red" | "blue" | "green" | "yellow"` となりそうである。だが実際は `"blue" | "green"` が期待される。
これは[[条件付き型は分配的 (TypeScript)|条件付き型は分配的]]なことから算出された結果であり、具体的には以下のような解釈をする。
1. [[ユニオン型 (TypeScript)|ユニオン型]]の1つ1つに対して型を算出する
2. 1の結果をすべてマージした[[ユニオン型 (TypeScript)|ユニオン型]]を結果とする
### 1について
以下の`extends`より手前を分解する。
```ts
"red" | "blue" | "green" | "yellow" extends "red" | "yellow" ? never : "red" | "blue" | "green" | "yellow";
```
すると、4つの文ができる。
```ts
"red" extends "red" | "yellow" ? never : "red";
"blue" extends "red" | "yellow" ? never : "blue";
"green" extends "red" | "yellow" ? never : "green";
"yellow" extends "red" | "yellow" ? never : "yellow";
```
それぞれの結果は以下のようになる。
```ts
never
"blue"
"green"
never
```
### 2について
これらをマージした[[ユニオン型 (TypeScript)|ユニオン型]]を作る。
```ts
never | "blue" | "green" | never
```
[[TypeScriptのnever型をユニオン型の一部に指定しても結果は変わらない]]ため、これは `"blue" | "green"` となり、期待値と一致する。