@cody-greene/prop-types

The lovable React.PropTypes interface, modified for general use

Usage no npm install needed!

<script type="module">
  import codyGreenePropTypes from 'https://cdn.skypack.dev/@cody-greene/prop-types';
</script>

README

The loveable React.PropTypes interface, modified for general use. The Error messages generated

// node or browser
const PropTypes = require('prop-types')

Built-in types

Simple: bool, string, number, array, object, func Complex:

  • oneOf(['enum1', 'enum2'): Value must strictly equal (===) one of these enumerated values. Adding null or undefined to the list will make the value optional.

  • oneOfType([PropTypes.*, ...]): At least one validator must succeed

  • objectOf(PropTypes.*): Every value in the obect should be the same type

  • arrayOf(PropTypes.*): Every element in the array should be the same type

  • shape({...}): An object taking on a particular shape. If all props are optional, then the object itself is optional. The returned validator, unlike any other builtin, only requires two arguments validate(val, displayName).

bool, oneOf, shape: do not directly support .isRequired

Example usage (basic)

const validate = PropTypes.shape({
  foo: PropTypes.number.isRequired,
  bar: PropTypes.arrayOf(PropTypes.string),
  baz: PropTypes.oneOf(['green', 'red', 'blue'])
})
let err = validate({
  foo: 29,
  bar: ['x', 'y', 7],
  baz: 'green'
}, 'MyDisplayName')
console.log(err.toString())
// TypeError: MyDisplayName: ".baz[2]" is number, expected string

Custom types

Remeber, these type definitions are all just functions so you can write your own too! Use PropTypes.optional(fn) to wrap your base fn with a null/undefined check and expose the original fn as .isRequired

/**
 * @param {any} val
 * @param {string} displayName
 * @param {string} key Optional with PropTypes.shape validator
 * @param {object} props Optional with PropTypes.shape validator
 * @return {Error|null}
 * @example customChecker(val, displayName, key, props)
 * note: Checkers should have very shallow call-stacks
 *       Avoid using things like Array.forEach() here
 */
const myLuckyNumber = PropTypes.optional(function (val, displayName, key, props) {
  if (val !== 7) return new TypeError(`${displayName}: ${key} is not 7`)
})
const validate = PropTypes.shape({
  foo: myLuckyNumber.isRequired,
  bar: PropTypes.arrayOf(myLuckyNumber)
})

Example usage (validate background worker params)

Add parameter validation to job handlers for something like [node-resque][] or [arquebus][] where JSON.parse() is used to generate input. This pattern could also apply to querystring parameters when writing a REST API, or untrusted input when building a dynamic SQL statement.

/**
 * Define a background job similar to how React Components are defined
 * @param {string} displayName
 * @param {object} propTypes
 * @param {function} perform(props, done)
 * @return {function}
 */
function createJob(opt) {
  const validate = PropTypes.shape(opt.propTypes)
  return function validateAndRun(props, done) {
    let err = validate(props, opt.displayName, 'props')
    if (err) done(err)
    else opt.perform(props, done)
  }
}

const calsync = createJob({
  displayName: 'CalendarSync',
  propTypes: {
    uid: PropTypes.string.isRequired,
    fromDate: PropTypes.number.isRequired,
    private: PropTypes.bool
  },
  perform(props, done) {
    console.log(props)
    done()
  }
}

// Register the job handler with node-resque or arquebus
// Or just run it directly for the sake of a short example
calsync({
  uid: '010101010101',
  private: true
}, function (err) {
  if (err) console.log(err.stack)
  else console.log('done!')
})
// TypeError: CalendarSync "props.fromDate" is required