How to iterate over TypeScript union types
- Published at
- Updated at
- Reading time
- 2min
Is it just me, or are TypeScript conditional types and the extends
keyword kinda scary?
ts
// What is going on? 😅typeSomething <T > =T extends number ? never :T ;
I've just read Dr. Axel's "Conditional types in TypeScript" and some things finally clicked for me.
Let's say we have a union type containing multiple strings.
ts
typeAllColors = "Black" | "White" | "Orange" | "Red" | "Blue" | "Yellow" | "Gray";
Some people don't consider black, white and gray as colors. How could you now iterate over the union type and get these "noncolors" out of the AllColors
type?
ts
// Iterate of the types included in `AllColors`.//// If type `T` does not extend `R` apply the `never` type.// `never` will remove `T` from the resulting union type.typeRemove <T ,R > =T extendsR ? never :T ;// Remove "Black" and "White" from "AllColors" union type.typeRealColors =Remove <AllColors , "Black" | "White" | "Gray">;
Using conditional types, you can iterate over and filter union types. If you return never
, it will be excluded from the resulting union type.
TypeScript includes the built-in utility types exclude
and extract
for these use cases, and I only picked this example to explain the concept.
That's pretty cool, but you can use conditional types also to iterate over a union type and map the resulting types.
In this example, the strings are prefixed with String:
.
ts
typeRandom = "Joo" | "Foo" | 123 | 234;// Iterate of the types included in `Random`.//// If type `T` is of type `string` prefix it.typePrefix <T > =T extends string ? `String: ${T }` :T ;typeMappedTypes =Prefix <Random >;
Or, if we take the color example, we could also iterate and append (noColor)
to Black
, White
and Gray
.
ts
typeAllColors = "Black" | "White" | "Orange" | "Red" | "Blue" | "Yellow" | "Gray";// Iterate of the types included in `AllColors`.//// If type `T` is of type `string` and `T` is of type `R`// append parentheses to the string type.typeSuffix <T ,R > =T extends string?T extendsR ? `${T } (no color)` :T :T ;typeMappedColors =Suffix <AllColors , "Black" | "White" | "Gray">;
Granted, the nested ternary isn't pretty, but it seems to be the only way to include two conditions to TypeScript's conditional types.
And as a last trick: if your union type only includes strings, you can spare all this extends
dance and use template literal types.
ts
typeAllColors = "Black" | "White" | "Orange" | "Red" | "Blue" | "Yellow" | "Gray";typeAllPrefixedColors = `Color: ${AllColors }`;
Good stuff!
Join 5.7k readers and learn something new every week with Web Weekly.