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:
UserShapecan be used only with other React componentspropTypesthrows a warning during runtimewidthandheightshould 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() { /* ... */ }
}
Useris 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' |};