Union types. They enable cool things and can eliminate an entire class of bugs that would only otherwise be discovered at runtime.
They look like this:
type Key = string | number | symbol;
type Units = "${number}px" | "${number}%" | "none";
type Shape =
| { name: "circle"; radius: number }
| { name: "square"; side: number }
| { name: "rectangle"; height: number; width: number };The Typescript website's definition of a union type: A union type describes a value that can be one of several types. We use the vertical bar (|) to separate each type, so number | string | boolean is the type of a value that can be a number, a string, or a boolean.
If you're familiar with sum types in Haskell or enum types in Rust, you already know union types.
π The example I'm providing is of a React component and its
Propstype, but this pattern is not specific to React, nor the frontend!
Values depend on the value of other values all the time. In the case of a component, you might see something like this in a PayIt frontend app:
type Props = {
alt?: string; // This must be provided if `label` is undefined!
label?: ReactChild;
}
export const Component = (props: Props) => {The comment warns us alt must be defined if label is undefined (presumably to ensure accessibility), but there's nothing to stop me from using this component incorrectly. What's worse, I won't know about it until the code actually runs. (and, in practice, sometimes we're lucky just to have a type definition, much less one with a descriptive comment)
<Component /> // BAD: I should be providing an `alt` prop!We can encode Props' intra-type dependency between alt on the value of label in the type itself.
type Props =
| { label: ReactChild }
| { alt: string; label?: undefined };And now:
<Component /> // β Type error: Property `alt` is missing... π
<Component alt="alt hello" /> // β
<Component label="Hello" /> // β
Typescript's control flow analysis also applies the rules when we try to do something with a value of our type.
e.g. given a value props of type Props, let's say we want to trim any white space off alt:
props.alt.trim(); // β Type error: Property 'alt' does not exist on type '{ label: ReactChild }'The compiler just saved us from a potential bug.
if (props.label) {
props.alt.trim(); // π
}