back

Discriminate types

Recently, I have stumbled upon the need of creating a type for component's props that would work differently depending on props provided.
I wanted to implement a type in a way that when some prop value is set, another value should conform to a certain type, but if the first one is missing one should not be able to set any value to a second prop. As an example, imagine you have a button component which has optional icon. You can define if icon is visible as well as its color. But does it even make sense to define a color if the first prop is not provided or falsy at all. So, let us describe the generic prop for such a button component:
interface ButtonProps {
  onClick: () => void
  label: string
}
Here we need to understand that for defining icon props we would need to handle two states of button props - with icon and without. Let's do the following:
interface ButtonWithIconProps extends ButtonProps {
  withIcon?: true
  iconColor?: string
}
Here we extended the general props of button component and defined the state with icon. From that point we can also define the icon color with ease. In this scenario we wanted to define two states only because boolean type could be described by two literal types in Typescript: false and true But as I mentioned below, we also need a second state without an icon:
interface ButtonWithoutIconProps extends ButtonProps {
  withIcon?: false
  iconColor?: never
}
This is much more interesting, right? Once we make the withIcon prop falsy, we would not be able to provide any value as an icon color.
To sum this up, we should change our button component props to the following:
type ButtonComponentProps = ButtonWithIconProps | ButtonWithoutIconProps
Here is the union type and depending on provided props typescript will do the magic :)
To play more with Discriminated Unions follow this link