facebook / prop-types

Runtime type checking for React props and similar objects
MIT License
4.48k stars 356 forks source link

Mixed arrayOf prop type #239

Closed dextermb closed 5 years ago

dextermb commented 5 years ago

I am currently working on a project where we pass in an array with mixed types. Here's an example:

const dataToPassIn = [
    {},
    [],
];

This is an array that contains both arrays and objects. I've attempted to use oneOfType and multiple arrayOf but still seem to get invalid prop errors. Here's an example:

PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.object),
    PropTypes.arrayOf(PropTypes.array),
])

What would be the correct/best way to validate this prop? Both the objects and arrays within the top level array direct children so I don't think PropType.shape would work.

Cheers.

DivineOmega commented 5 years ago

I believe you can do something similar to the following.

myProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (typeof propValue !== 'object' && typeof propValue !== 'array') {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to `' + componentName + '`. Validation failed.'
      );
    }
  })
dextermb commented 5 years ago

I believe you can do something similar to the following.

myProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (typeof propValue !== 'object' && typeof propValue !== 'array') {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to `' + componentName + '`. Validation failed.'
      );
    }
  })

While this is a good solution I'd love to see PropTypes.arrayOfTypes implemented or something similar

ljharb commented 5 years ago

Your original approach should be inverted:

PropTypes.arrayOf(
  PropTypes.oneOfType([
    PropTypes.arrayOf(arrayItemsPropType),
    PropTypes.shape(objectItemsShape),
  ])
)

In other words, it’s always an array - but the array can hold either an array or an object.

DivineOmega commented 5 years ago

@ljharb This is far better than my suggestion. I wasn't aware this syntax was supported.

alimirakim commented 3 years ago

Your original approach should be inverted:

PropTypes.arrayOf(
  PropTypes.oneOfType([
    PropTypes.arrayOf(arrayItemsPropType),
    PropTypes.shape(objectItemsShape),
  ])
)

In other words, it’s always an array - but the array can hold either an array or an object.

Thanks so much, still helping out folks like me years later :) .

papuruth commented 3 years ago

How to validate this const children = [{//react node}, false] || [{//react node}, {//react node}]

  PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.node, PropTypes.bool])),
ljharb commented 3 years ago

@papuruth i'm not sure exactly what your pseudocode means; if you want a tuple, of length 2, where the first item is always a node and the second either a node or false, then you can't really do that with this package (your code is the best you can do).

I'd suggest using https://npmjs.com/airbnb-prop-types, which allows you to compose things together to make what you want.

ctsstc commented 3 years ago

It would be interesting if it could support something like this:

PropTypes.oneOfType([
  PropTypes.exact([PropTypes.element, PropTypes.element]),
  PropTypes.exact([PropTypes.element, PropTypes.bool]),
])

// or
PropTypes.exact([
  PropTypes.element,
  PropTypes.oneOfType([PropTypes.element, PropTypes.bool])
])

Edit: @papuruth consider PropTypes.element over PropTypes.node if you want it to be a React element rather than any html element.

papuruth commented 3 years ago

Thanks @ljharb @ctsstc I'll try these

godcodevo commented 11 months ago

Thank you!