It's not uncommon to have a React component that takes props where some of those props are mutually exclusive. Let's imagine a case where we have a Button
component. We want our button to allow for the following possibilities:
- The button has text.
- The button has text and an icon.
- The button has an icon, but no text.
An initial implementation might look like:
const Button = ({ text, icon }: { text?: string; icon?: string }) => {
// Handle button rendering here...
};
...but that will allow you to render the component like this: <Button />
with no errors, which is not ideal. You need to provide at least something to render!
We can solve this by using Discriminated Unions, but the non-obvious part is how we'll be using never
to allow TypeScript to actually do the discrimination. If we implement this:
type WithText = {
text: string;
icon?: string;
}
type WithOnlyIcon = {
text?: never; // <-- This is the important bit.
icon: string;
}
type Props = WithText | WithOnlyIcon;
const Button = ({ text, icon }: Props) => {
// Handle button rendering here...
};
Now, you'll get a TypeScript error if you try to render a Button
with no icon
and no text
!
Here's a playground to explore this further.