項目53 条件型のユニオンでの分配を制御する
項目52 オーバーロードシグネチャより条件型を優先的に使用するで、条件型によってユニオンの分配が望ましいことをまとめたが、そうでないケースが有る
条件に型がそのまま使われていない場合
条件型でユニオンの分配がされるのは、条件に型がそのまま使われている場合(T extends ...)に限る
条件型によって不要にユニオンが分配されるケースが有る
その場合、不要な分配を抑制する必要がある
Tをタプル型にラップして[T]にする
code:ts
type Comparable<T> =
T extends Date ? Date | number:
T extends number ? number :
T extends string ? string :
never;
条件型が思うように分配されないときの対応
T型のN個の要素からなるタプルを作るNTuple<T, N>というジェネリック型を例にする
アキュムレーターを使う(Acc)
code:ts
type NTuple<T, N extends number> =
N extends number
? NTupleHelp<T, N, []>
: never;
type NTupleHelp<T, N extends number, Acc extends T[]> =
Acc'length' extends N
? Acc
: NTupleHelp<T, N, T, ...Acc>;
type PairOrTriple = NTuple<bigint, 2 | 3>;
// ^? type PairOrTriple = bigint, bigint | bigint, bigint, bigint
逆に下記のコードは、分配されない
条件がAcc['length'] extends Nであり、分配に必要なN extends ...で始まっていないため、上記のコードは、先にN extends ... の制約が存在しているため、分配される
code:ts
type NTuple<T, N extends number> = NTupleHelp<T, N, []>;
type NTupleHelp<T, N extends number, Acc extends T[]> =
Acc'length' extends N
? Acc
: NTupleHelp<T, N, T, ...Acc>;
type PairOrTriple = NTuple<bigint, 2 | 3>;
// ^? type PairOrTriple = bigint, bigint
boolean型とnever型での分配の注意点
boolean型
TypeScriptはboolean型を内部的にユニオンとして扱っている
code:ts
type boolean = true | false;
なので下記のコードは誤った型推論が行われる
CelebrateIfTrue<boolean>がtrueの条件が適応される
code:ts
type CelebrateIfTrue<V> = V extends true ? 'Huzzah!' : never;
type Party = CelebrateIfTrue<true>;
// ^? type Party = "Huzzah!"
type NoParty = CelebrateIfTrue<false>;
// ^? type NoParty = never
type SurpriseParty = CelebrateIfTrue<boolean>;
// ^? type SurpriseParty = "Huzzah!"
下記のようにすれば誤った分配を防ぐことができる
code:ts
type CelebrateIfTrue<V> = V extends true ? 'Huzzah!' : never;
type SurpriseParty = CelebrateIfTrue<boolean>;
// ^? type SurpriseParty = never
never型
TypeScriptはnever型を空のユニオンとして扱う
分配する対象がないため、空集合(never)が返される
code:ts
type AllowIn<T> = T extends {password: "open-sesame"} ? "Yes" : "No";
type N = AllowIn<never>;
// ^? type N = never
#TypeScript