react/packages/shared/checkPropTypes.js

110 lines
3.8 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
const loggedTypeFailures: {[string]: boolean} = {};
import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import hasOwnProperty from 'shared/hasOwnProperty';
const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
function setCurrentlyValidatingElement(element: any) {
if (__DEV__) {
if (element) {
const owner = element._owner;
const stack = describeUnknownElementTypeFrameInDEV(
element.type,
element._source,
owner ? owner.type : null,
);
ReactDebugCurrentFrame.setExtraStackFrame(stack);
} else {
ReactDebugCurrentFrame.setExtraStackFrame(null);
}
}
}
export default function checkPropTypes(
typeSpecs: Object,
values: Object,
location: string,
componentName: ?string,
element?: any,
): void {
if (__DEV__) {
// $FlowFixMe This is okay but Flow doesn't know it.
const has = Function.call.bind(hasOwnProperty);
for (const typeSpecName in typeSpecs) {
if (has(typeSpecs, typeSpecName)) {
let error;
// Prop type validation may throw. In case they do, we don't want to
// fail the render phase where it didn't fail before. So we log it.
// After these have been cleaned up, we'll let them throw.
try {
// This is intentionally an invariant that gets caught. It's the same
// behavior as without this statement except with a better message.
if (typeof typeSpecs[typeSpecName] !== 'function') {
// eslint-disable-next-line react-internal/prod-error-codes
const err = Error(
(componentName || 'React class') +
': ' +
location +
' type `' +
typeSpecName +
'` is invalid; ' +
'it must be a function, usually from the `prop-types` package, but received `' +
typeof typeSpecs[typeSpecName] +
'`.' +
'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.',
);
err.name = 'Invariant Violation';
throw err;
}
error = typeSpecs[typeSpecName](
values,
typeSpecName,
componentName,
location,
null,
'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED',
);
} catch (ex) {
error = ex;
}
if (error && !(error instanceof Error)) {
setCurrentlyValidatingElement(element);
console.error(
'%s: type specification of %s' +
' `%s` is invalid; the type checker ' +
'function must return `null` or an `Error` but returned a %s. ' +
'You may have forgotten to pass an argument to the type checker ' +
'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
'shape all require an argument).',
componentName || 'React class',
location,
typeSpecName,
typeof error,
);
setCurrentlyValidatingElement(null);
}
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
// Only monitor this failure once because there tends to be a lot of the
// same error.
loggedTypeFailures[error.message] = true;
setCurrentlyValidatingElement(element);
console.error('Failed %s type: %s', location, error.message);
setCurrentlyValidatingElement(null);
}
}
}
}
}