When a conditional type acts on a naked type parameter, TypeScript distributes it over each member of a union:
type IsString<T> = T extends string ? true : false;
type R = IsString<string | number>;
// → true | false (applied to each member separately)
// The most famous distributive utility:
type NonNullable<T> = T extends null | undefined ? never : T;
type R2 = NonNullable<string | null | undefined | number>;
// → string | numberTo prevent distribution, wrap in a tuple:
type Strict<T> = [T] extends [string] ? true : false;
type R3 = Strict<string | number>;
// → false (the whole union is checked at once)Implement three runtime utilities that mirror distributive type operations:
| Function | Behaviour | |
|---|---|---|
filterUnion(values, type) | Keep only values where typeof v === type | |
excludeNullish(arr) | Remove null and undefined (mirrors NonNullable<T>) | |
flattenUnion(arr) | Flatten one level: mixed `T | T[] → T[]` |
Sample tests