Fabian Lurz Fabian Lurz - 1 month ago 7
Javascript Question

How to typecheck properties of an object prop in React?

I have following that i want to annotate with flow:

type PropType = {
content: Object
};

export const DialogContent = ({ content }: PropType) => (
<div>
<p className={cn('text-head')}>{content.h4}</p>
<p className={cn('text-bottom')}>
{content.p}
</p>
</div>
);


I know how to do the type-check to so that
content
is of type
Object
(as shown above), but how can I type-check its properties as well?




Already tried this:

type PropType = {
content: {
p: string,
h4: string
}
};


But then flow just complains that
p
and
h4
is never used.

Answer

So you want to send in an prop that is of type object, which has to have the properties p and h4?

This isn't possible without writing a custom function that does this checking. To do this you would declare your propTypes like so:

propTypes: {
  content: function(props, propName, componentName) {
    //do your validation here. 
    //Return an Error if something's wrong, otherwise don't return anything (or return null).
  }
}

Here's what the official docs say:

You can also specify a custom validator. It should return an Error object if the validation fails. Don't console.warn or throw, as this won't work inside oneOfType.

Read more about typechecking with PropTypes on the Official Documentation.


Demo

Here's a demo I prepared. It may or may not be overkill for what you're looking for, since the validation is quite extensive. You may cherry-pick the ones you need. The validations below for your content are (in order):

  • Verify the prop content is passed.
  • Verify the prop content is an object.
  • Verify the prop content has an object property of p.
  • Verify the prop content has an object property of h1.
  • Verify the object property content.p is a string.
  • Verify the object property content.h1 is a string.

var DialogContent = React.createClass({
  propTypes: {
    content: function(props, propName, componentName) {
      if (!props.content) {
        return new Error(
          'Required prop `' + propName + '` was not specified in `' + componentName + '`.'
        );
      } else if (typeof props.content !== 'object') {
        return new Error(
          'Invalid prop `' + propName + '` of type `' + typeof props.content + '` supplied to `' + componentName + '`, expected `object`.'
        );
      } else if (!props.content.p) {
        return new Error(
          'Required prop `p` of object `' + propName + '` was not specified in `' + componentName + '`.'
        );
      } else if (!props.content.h1) {
        return new Error(
          'Required prop `h1` of object `' + propName + '` was not specified in `' + componentName + '`.'
        );
      } else if (typeof props.content.p !== 'string') {
        return new Error(
          'Invalid object property `p` of prop `' + propName + '` of type `' + typeof props.content.p + '` supplied to `' + componentName + '`, expected `string`.'
        );
      } else if (typeof props.content.h1 !== 'string') {
        return new Error(
          'Invalid object property `h1` of prop `' + propName + '` of type `' + typeof props.content.h1 + '` supplied to `' + componentName + '`, expected `string`.'
        );
      }
    }
  },

  render: function() {
    return <div>My DialogContent Component</div>;
  }
});

var obj = {
  p: "foo",
  h1: "bar"
};

ReactDOM.render(<DialogContent content={obj} />,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="container"></div>

You may also test it on this Fiddle and do some mocking. Try changing the values, types and object properties passed into the component and read the console output.

Hope this helps. Good luck!