diff --git a/packages/react/src/ReactElementValidator.js b/packages/react/src/ReactElementValidator.js index f3f16f7649576..8fb5d2e6c5ddd 100644 --- a/packages/react/src/ReactElementValidator.js +++ b/packages/react/src/ReactElementValidator.js @@ -26,6 +26,8 @@ import ReactDebugCurrentFrame from './ReactDebugCurrentFrame'; if (__DEV__) { var currentlyValidatingElement = null; + var propTypesMisspellWarningShown = false; + var getDisplayName = function(element): string { if (element == null) { return '#empty'; @@ -212,11 +214,20 @@ function validatePropTypes(element) { } var name = componentClass.displayName || componentClass.name; var propTypes = componentClass.propTypes; - if (propTypes) { currentlyValidatingElement = element; checkPropTypes(propTypes, element.props, 'prop', name, getStackAddendum); currentlyValidatingElement = null; + } else if ( + componentClass.PropTypes !== undefined && + !propTypesMisspellWarningShown + ) { + propTypesMisspellWarningShown = true; + warning( + false, + 'Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?', + name || 'Unknown', + ); } if (typeof componentClass.getDefaultProps === 'function') { warning( diff --git a/packages/react/src/__tests__/ReactElementValidator-test.js b/packages/react/src/__tests__/ReactElementValidator-test.js index 5f0f348b995da..c18dea2c1a762 100644 --- a/packages/react/src/__tests__/ReactElementValidator-test.js +++ b/packages/react/src/__tests__/ReactElementValidator-test.js @@ -448,6 +448,29 @@ describe('ReactElementValidator', () => { } }); + it('should warn if component declares PropTypes instead of propTypes', () => { + spyOn(console, 'error'); + class MisspelledPropTypesComponent extends React.Component { + static PropTypes = { + prop: PropTypes.string, + }; + render() { + return React.createElement('span', null, this.props.prop); + } + } + + ReactTestUtils.renderIntoDocument( + React.createElement(MisspelledPropTypesComponent, {prop: 'Hi'}), + ); + if (__DEV__) { + expect(console.error.calls.count()).toBe(1); + expect(console.error.calls.argsFor(0)[0]).toBe( + 'Warning: Component MisspelledPropTypesComponent declared `PropTypes` ' + + 'instead of `propTypes`. Did you misspell the property assignment?', + ); + } + }); + it('should warn when accessing .type on an element factory', () => { spyOnDev(console, 'warn'); function TestComponent() { diff --git a/packages/react/src/__tests__/ReactJSXElementValidator-test.js b/packages/react/src/__tests__/ReactJSXElementValidator-test.js index d935fd707598b..82c2a60c4045d 100644 --- a/packages/react/src/__tests__/ReactJSXElementValidator-test.js +++ b/packages/react/src/__tests__/ReactJSXElementValidator-test.js @@ -484,4 +484,26 @@ describe('ReactJSXElementValidator', () => { ); } }); + + it('should warn if component declares PropTypes instead of propTypes', () => { + spyOn(console, 'error'); + class MisspelledPropTypesComponent extends React.Component { + render() { + return {this.props.prop}; + } + } + MisspelledPropTypesComponent.PropTypes = { + prop: PropTypes.string, + }; + ReactTestUtils.renderIntoDocument( + , + ); + if (__DEV__) { + expect(console.error.calls.count()).toBe(1); + expect(console.error.calls.argsFor(0)[0]).toBe( + 'Warning: Component MisspelledPropTypesComponent declared `PropTypes` ' + + 'instead of `propTypes`. Did you misspell the property assignment?', + ); + } + }); });