prop-types-exact
Version:
For use with React PropTypes. Will error on any prop not explicitly specified.
50 lines (40 loc) • 1.77 kB
JavaScript
;
var assign = require('object.assign');
var hasOwn = require('hasown');
var ownKeys = require('own-keys');
var TypeError = require('es-errors/type');
var isPlainObject = require('../build/helpers/isPlainObject');
var zeroWidthSpace = '\u200b';
var specialProperty = 'prop-types-exact: ' + zeroWidthSpace;
// eslint-disable-next-line no-restricted-properties
var semaphore = typeof Symbol === 'function' && typeof Symbol['for'] === 'function' ? Symbol['for'](specialProperty) : /* istanbul ignore next */ specialProperty;
var callBound = require('call-bound');
var isEnumerable = callBound('Object.prototype.propertyIsEnumerable');
/** @type {<T extends Function>(fn: T) => T} */
function brand(fn) {
// eslint-disable-next-line no-param-reassign
fn[specialProperty] = semaphore;
return fn;
}
function isBranded(value) {
return value && value[specialProperty] === semaphore;
}
module.exports = function forbidExtraProps(propTypes) {
if (!isPlainObject(propTypes)) {
throw new TypeError('given propTypes must be an object');
}
if (hasOwn(propTypes, specialProperty) && !isBranded(propTypes[specialProperty])) {
throw new TypeError('Against all odds, you created a propType for a prop that uses both the zero-width space and our custom string - which, sadly, conflicts with `prop-types-exact`');
}
var forbidden = {};
forbidden[specialProperty] = brand(function forbidUnknownProps(props, _, componentName) {
var unknownProps = ownKeys(props).filter(function (prop) {
return !hasOwn(propTypes, prop) && isEnumerable(props, prop);
});
if (unknownProps.length > 0) {
return new TypeError(componentName + ': unknown props found: ' + unknownProps.join(', '));
}
return null;
});
return assign({}, propTypes, forbidden);
};