DEV Community

Cover image for How to specify the shape of an object with PropTypes
Cesare Ferrari
Cesare Ferrari

Posted on

How to specify the shape of an object with PropTypes

With PropTypes we can also check the inner structure of objects

Yesterday we learned how the PropTypes library makes it easy for us to check the types of objects we pass to React components through the props.
For example, this code makes sure the object pokemons is an array of objects:

Pokemons.propTypes = {
  pokemons: PropTypes.arrayOf(PropTypes.object)
}
Enter fullscreen mode Exit fullscreen mode

If we accidentally pass pokemons as an array of other types, like an array of strings for example, we get a warning in the Javascript console and we can fix the error.

PropTypes lets us go further, though. It lets us describe in detail the inner structure of an object, what is called the shape of an object. This makes our data validations more thorough and accurate.
The way we do this deep validation is by using the shape() method of PropTypes.
shape() takes an object and validates the types inside the object.
Here's an example:

Pokemon.propTypes = {
  pokemon: PropTypes.shape({
    name: PropTypes.string,
    id: PropTypes.number,
    base_stamina: PropTypes.number,
    base_defense: PropTypes.number
  })
}
Enter fullscreen mode Exit fullscreen mode

Here we validate that the pokemon object has a name of type string, and id, base_stamina and base_defense of type number.
If the object we pass into our props has one of these properties wrong, we will get a warning, even though Javascript would be happy to accept a string in place of a number type.

Required properties

Sometimes we need to pass props objects that have some required properties and some optional ones.
PropTypes helps us in this case as well by specifying which property is required and raising a warning if it's not present.
To perform this check we simply add isRequired at the end of the property type, like so:

Pokemon.propTypes = {
  pokemon: PropTypes.shape({
    name: PropTypes.string,
    id: PropTypes.number.isRequired  // required property
  })
}
Enter fullscreen mode Exit fullscreen mode

We may not feel the need to use PropTypes every single time, but when our project grows and becomes complex PropTypes sure helps avoid bugs that could become difficult to track down.

Latest comments (6)

Collapse
 
vdsheryarshirazi profile image
vdsheryarshirazi

Hi,

I'm unable to create PropType for following prop.

[
  [
    "cat",
    {
      "color": "black",
      "price": "$2000",
    }
  ],
  [
    "dog",
    {
      "color": "brown",
      "price": "$3500",
    }
  ],
]
Enter fullscreen mode Exit fullscreen mode
Collapse
 
vdsheryarshirazi profile image
vdsheryarshirazi

resolved by

PropTypes.arrayOf(
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string.isRequired,
PropTypes.shape({
price: string,
color: string,
}),
])
)
)

Collapse
 
samthomson profile image
Sam Thomson

Hi, nice article thanks.
I have one question: How should objects be marked as required.
Let's say I have an object:

user:  PropTypes.Shape({
    id: PropTypes.string.isRequired,
    name: PropTypes.string.isRequired
})
Enter fullscreen mode Exit fullscreen mode

id/name are required for the/any user.
For the component in question I require there is a user at all.
So wonder is the above definition correct, or should I also add .isRequired to the whole object? Making it:

user:  PropTypes.Shape({
    id: PropTypes.string.isRequired,
    name: PropTypes.string.isRequired
}).isRequired
Enter fullscreen mode Exit fullscreen mode

So tl;dr; are objects that have required properties, automatically required themselves? Or are the properties only required If an object is passed in?

Collapse
 
cesareferrari profile image
Cesare Ferrari

Not sure if I understand your question correctly, but you would use PropTypes on a component, not an object.

So, if you have a User component, you would use PropTypes to enforce that the User component gets passed a prop named id and a prop named name, like this:

User.propTypes = {
  id: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired
}
Enter fullscreen mode Exit fullscreen mode

On the other hand, if you have a UserList component, that has a user prop that is an object, you would set id and name properties of the user object as isRequired and that would give you a warning if the user is not passed in at all.

UserList.propTypes = {
  user: PropTypes.shape({
    id: PropTypes.number.isRequired  // required property user
    name: PropTypes.string.isRequired // required property of user
  })
}
Enter fullscreen mode Exit fullscreen mode

Hope this makes sense.

Collapse
 
samthomson profile image
Sam Thomson

Hey, no sorry what I meant was the user was an object, I should have denoted it with the shape type.
Although I wasn't sure if the user would automatically be required if all of its props were required, like this:

user: PropTypes.shape({
    id: PropTypes.string.isRequired  // required property user
    name: PropTypes.string.isRequired // required property of user
})
Enter fullscreen mode Exit fullscreen mode

I figured out that in the above, those props are only required if the user is passed in at all. To also ensure a user is passed, not just a valid user I needed:

user: PropTypes.shape({
    id: PropTypes.string.isRequired
    name: PropTypes.string.isRequired
}).isRequired
Enter fullscreen mode Exit fullscreen mode

Shall leave it here in case it helps anyone else.

Thread Thread
 
thebuildguy profile image
Tulsi Prasad • Edited

Thanks Sam, my eslint was throwing error and this fixed it!