React + TypeScript: Interdependent prop types
Published on in JavaScript, React and TypeScript
Last updated on
I.e. props that depend on other props. You can require and forbid certain prop combinations by utilizing TypeScript's union and intersection types.
Table of contents
Example problem
Let's say we want to create a Shape
React component
with the following requirements:
- It takes a
color
prop. - It takes a
type
prop with the value of'circle'
,'square'
or'rectangle'
. - Depending on the
type
prop's value, it takes the following props as well:radius
when the type is'circle'
.width
when the type is'square'
.width
andheight
when the type is'rectangle'
.
A naive type for the props object would look like this:
type ShapeProps = {
color: string
type: 'circle' | 'square' | 'rectangle'
radius: number
width: number
height: number
}
function Shape(props: ShapeProps) {
return /* ... */
}
It's naive because we could specify all of those props:
<Shape color="red" type="circle" radius={10} width={20} height={30} />
That makes no sense because a circle shape doesn't have a width or a height; it only has a radius.
The other shapes have similar problems.
Union type to the rescue
We instead want to create a union type:
type ShapeProps =
| { color: string; type: 'circle'; radius: number }
| { color: string; type: 'square'; width: number }
| { color: string; type: 'rectangle'; width: number; height: number }
Now TypeScript won't let us specify invalid props.
For example, when the type is 'circle'
:
- We can (and must) specify only the
color
andradius
props. - We can't specify the
width
orheight
props; doing so would produce an error.
Simplify with an intersection type
Notice how we are repeating the color
prop in all three types.
We can reduce duplication by using an intersection type:
type ShapeProps = { color: string } & (
| { type: 'circle'; radius: number }
| { type: 'square'; width: number }
| { type: 'rectangle'; width: number; height: number }
)
More tricks
I learned this trick from Bruno Antunes's YouTube video React.js TypeScript Conditional Props – Props that depend on other Props (26:13). Check it out, it has more advanced examples as well.