Flow as Replacement for PropTypes
A couple of months ago at Product Hunt, we decided to switch from React.PropTypes to Flow. Initially, we started using Flow just for function definitions. Then we start replacing propType
.
Why we do did that? Let's say we have an UserImage
component with two possible uses:
<UserImage user={user} width={50} height={30} />
<UserImage user={user} variant="small" />
Its definition would be something like this:
const UserShape = {
id: React.PropTypes.number.isRequired,
name: React.PropTypes.name.isRequired,
};
class UserImage extends React.Component {
static propTypes = {
user: React.PropTypes.shape(UserShape).isRequired,
width: React.PropTypes.number,
height: React.PropTypes.number,
variant: React.PropTypes.oneOf(['big', 'medium', 'small')
};
render() { /* ... */ }
}
There are some problems with that definition:
UserShape
can be used only with other React componentspropTypes
throws a warning during runtimewidth
andheight
should be passed together, but we can't enforce that- It is possible to pass all three props -
width
,height
,variant
Here is how the Flow solves those problems:
type User = {
id: number,
name: string,
};
class UserImage extends React.Component {
props: {
user: User,
width: number,
height: number,
} | {
user: User,
variant: 'big' | 'medium' | 'small',
};
render() { /* ... */ }
}
User
is a generic type (can be used for any javascript function)- Flow types are used only during build
<UserImage user={user} width="10" />
breaks the build ?
Unfortunately this is still possible:
<UserImage user={user} width={50} height={30} variant="small" />
One way to solve that is the following:
props: {
user: User,
width: number,
height: number,
// expects "variant" is not passed
variant?: void,
} | {
user: User,
variant: 'big' | 'medium' | 'small',
// expects "width" and "height" are not passed
width?: void,
height?: void,
};
It's a bit ugly, but it gets the job done:
<UserImage user={user} width={50} height={30} variant="small" />
^^^^^^^ number. This type is incompatible with void
<UserImage user={user} width={50} height={30} variant="small" />
^^^^^^^ number. This type is incompatible with void
<UserImage user={user} width={50} height={30} variant="small" />
^^^^^^^ string. This type is incompatible with void
For more information check Flow documentation.
p.s. Gabriele Petronella and Vjeux told me on twitter about $Exact
and {| |}
:
props:
{| user: User, width: number, height: number |} |
{| user: User, variant: 'big' | 'medium' | 'small' |};