Conditional types
| T extends U ? A : B | |
| Distributivity | |
| Filter with never | |
| Read API types |
T extends U ? A : B
A conditional type picks one of two types by testing another type. Read T extends U ? A : B as if T fits in U then A else B.
IsString answers yes for string and no for number. The test runs on types only and vanishes from the compiled code.
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"
Distributivity
When the tested type is a naked parameter like T, the condition distributes over unions. Each member is tested alone and the answers are joined back with a union.
ToArray sees string and number separately, so C becomes a string array or a number array. Wrap T in a tuple to stop this when you need the whole union at once.
type ToArray<T> = T extends unknown ? T[] : never;
type C = ToArray<string | number>; // string[] | number[]
Filter with never
never is the type with no values, so union members mapped to never simply disappear. That makes never a handy filter.
NoStrings drops string and keeps the rest, leaving number or boolean. Exclude in the next section is built on exactly this trick.
type NoStrings<T> = T extends string ? never : T;
type D = NoStrings<string | number | boolean>; // number | boolean
Read API types
Real code reads API unions with Exclude and Extract instead of hand-written conditions. Exclude drops the listed members while Extract keeps only the listed ones.
NotOk holds every status except ok, and OkOrLoading holds the two retryable ones. Both helpers are conditional types under the hood.
type Status = "ok" | "error" | "loading";
type NotOk = Exclude<Status, "ok">; // "error" | "loading"
type OkOrLoading = Extract<Status, "ok" | "loading">; // "ok" | "loading"
Next: infer
Article author: Arthur Isaev