diff --git a/src/core/ReactPropTypes.js b/src/core/ReactPropTypes.js index 42fbdb0ef55bd..6388ea5d95656 100644 --- a/src/core/ReactPropTypes.js +++ b/src/core/ReactPropTypes.js @@ -14,6 +14,7 @@ var ReactElement = require('ReactElement'); var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames'); +var deprecated = require('deprecated'); var emptyFunction = require('emptyFunction'); /** @@ -65,6 +66,9 @@ var emptyFunction = require('emptyFunction'); var ANONYMOUS = '<>'; +var elementTypeChecker = createElementTypeChecker(); +var nodeTypeChecker = createNodeChecker(); + var ReactPropTypes = { array: createPrimitiveTypeChecker('array'), bool: createPrimitiveTypeChecker('boolean'), @@ -75,13 +79,28 @@ var ReactPropTypes = { any: createAnyTypeChecker(), arrayOf: createArrayOfTypeChecker, - component: createComponentTypeChecker(), + element: elementTypeChecker, instanceOf: createInstanceTypeChecker, + node: nodeTypeChecker, objectOf: createObjectOfTypeChecker, oneOf: createEnumTypeChecker, oneOfType: createUnionTypeChecker, - renderable: createRenderableTypeChecker(), - shape: createShapeTypeChecker + shape: createShapeTypeChecker, + + component: deprecated( + 'React.PropTypes', + 'component', + 'element', + this, + elementTypeChecker + ), + renderable: deprecated( + 'React.PropTypes', + 'renderable', + 'node', + this, + nodeTypeChecker + ) }; function createChainableTypeChecker(validate) { @@ -151,13 +170,13 @@ function createArrayOfTypeChecker(typeChecker) { return createChainableTypeChecker(validate); } -function createComponentTypeChecker() { +function createElementTypeChecker() { function validate(props, propName, componentName, location) { if (!ReactElement.isValidElement(props[propName])) { var locationName = ReactPropTypeLocationNames[location]; return new Error( `Invalid ${locationName} \`${propName}\` supplied to ` + - `\`${componentName}\`, expected a React component.` + `\`${componentName}\`, expected a ReactElement.` ); } } @@ -238,13 +257,13 @@ function createUnionTypeChecker(arrayOfTypeCheckers) { return createChainableTypeChecker(validate); } -function createRenderableTypeChecker() { +function createNodeChecker() { function validate(props, propName, componentName, location) { - if (!isRenderable(props[propName])) { + if (!isNode(props[propName])) { var locationName = ReactPropTypeLocationNames[location]; return new Error( `Invalid ${locationName} \`${propName}\` supplied to ` + - `\`${componentName}\`, expected a renderable prop.` + `\`${componentName}\`, expected a ReactNode.` ); } } @@ -276,11 +295,8 @@ function createShapeTypeChecker(shapeTypes) { return createChainableTypeChecker(validate, 'expected `object`'); } -function isRenderable(propValue) { +function isNode(propValue) { switch(typeof propValue) { - // TODO: this was probably written with the assumption that we're not - // returning `this.props.component` directly from `render`. This is - // currently not supported but we should, to make it consistent. case 'number': case 'string': return true; @@ -288,13 +304,13 @@ function isRenderable(propValue) { return !propValue; case 'object': if (Array.isArray(propValue)) { - return propValue.every(isRenderable); + return propValue.every(isNode); } if (ReactElement.isValidElement(propValue)) { return true; } for (var k in propValue) { - if (!isRenderable(propValue[k])) { + if (!isNode(propValue[k])) { return false; } } diff --git a/src/core/__tests__/ReactPropTypes-test.js b/src/core/__tests__/ReactPropTypes-test.js index 21df8b33dcca7..1753d6ec32cfc 100644 --- a/src/core/__tests__/ReactPropTypes-test.js +++ b/src/core/__tests__/ReactPropTypes-test.js @@ -224,7 +224,7 @@ describe('ReactPropTypes', function() { beforeEach(function() { Component = React.createClass({ propTypes: { - label: PropTypes.component.isRequired + label: PropTypes.element.isRequired }, render: function() { @@ -235,16 +235,16 @@ describe('ReactPropTypes', function() { }); it('should support components', () => { - typeCheckPass(PropTypes.component,
); + typeCheckPass(PropTypes.element,
); }); it('should not support multiple components or scalar values', () => { var message = 'Invalid prop `testProp` supplied to `testComponent`, ' + - 'expected a React component.'; - typeCheckFail(PropTypes.component, [
,
], message); - typeCheckFail(PropTypes.component, 123, message); - typeCheckFail(PropTypes.component, 'foo', message); - typeCheckFail(PropTypes.component, false, message); + 'expected a ReactElement.'; + typeCheckFail(PropTypes.element, [
,
], message); + typeCheckFail(PropTypes.element, 123, message); + typeCheckFail(PropTypes.element, 'foo', message); + typeCheckFail(PropTypes.element, false, message); }); it('should be able to define a single child as label', () => { @@ -262,13 +262,13 @@ describe('ReactPropTypes', function() { }); it("should be implicitly optional and not warn without values", function() { - typeCheckPass(PropTypes.component, null); - typeCheckPass(PropTypes.component, undefined); + typeCheckPass(PropTypes.element, null); + typeCheckPass(PropTypes.element, undefined); }); it("should warn for missing required values", function() { - typeCheckFail(PropTypes.component.isRequired, null, requiredMessage); - typeCheckFail(PropTypes.component.isRequired, undefined, requiredMessage); + typeCheckFail(PropTypes.element.isRequired, null, requiredMessage); + typeCheckFail(PropTypes.element.isRequired, undefined, requiredMessage); }); }); @@ -349,20 +349,21 @@ describe('ReactPropTypes', function() { it('should warn for invalid values', function() { var failMessage = 'Invalid prop `testProp` supplied to ' + - '`testComponent`, expected a renderable prop.'; - typeCheckFail(PropTypes.renderable, true, failMessage); - typeCheckFail(PropTypes.renderable, function() {}, failMessage); - typeCheckFail(PropTypes.renderable, {key: function() {}}, failMessage); + '`testComponent`, expected a ReactNode.'; + typeCheckFail(PropTypes.node, true, failMessage); + typeCheckFail(PropTypes.node, function() {}, failMessage); + typeCheckFail(PropTypes.node, {key: function() {}}, failMessage); }); it('should not warn for valid values', function() { - typeCheckPass(PropTypes.renderable,
); - typeCheckPass(PropTypes.renderable, false); - typeCheckPass(PropTypes.renderable, ); - typeCheckPass(PropTypes.renderable, 'Some string'); - typeCheckPass(PropTypes.renderable, []); - typeCheckPass(PropTypes.renderable, {}); - typeCheckPass(PropTypes.renderable, [ + typeCheckPass(PropTypes.node,
); + typeCheckPass(PropTypes.node, false); + typeCheckPass(PropTypes.node, ); + typeCheckPass(PropTypes.node, 'Some string'); + typeCheckPass(PropTypes.node, []); + typeCheckPass(PropTypes.node, {}); + + typeCheckPass(PropTypes.node, [ 123, 'Some string',
, @@ -371,7 +372,7 @@ describe('ReactPropTypes', function() { ]); // Object of rendereable things - typeCheckPass(PropTypes.renderable, { + typeCheckPass(PropTypes.node, { k0: 123, k1: 'Some string', k2:
, @@ -384,26 +385,30 @@ describe('ReactPropTypes', function() { }); it('should not warn for null/undefined if not required', function() { - typeCheckPass(PropTypes.renderable, null); - typeCheckPass(PropTypes.renderable, undefined); + typeCheckPass(PropTypes.node, null); + typeCheckPass(PropTypes.node, undefined); }); it('should warn for missing required values', function() { typeCheckFail( - PropTypes.renderable.isRequired, + PropTypes.node.isRequired, null, 'Required prop `testProp` was not specified in `testComponent`.' ); typeCheckFail( - PropTypes.renderable.isRequired, + PropTypes.node.isRequired, undefined, 'Required prop `testProp` was not specified in `testComponent`.' ); }); - it('should accept empty array & object for required props', function() { + it('should accept empty array for required props', function() { + typeCheckPass(PropTypes.node.isRequired, []); + }); + + it('should still work for deprecated typechecks', function() { + typeCheckPass(PropTypes.renderable, []); typeCheckPass(PropTypes.renderable.isRequired, []); - typeCheckPass(PropTypes.renderable.isRequired, {}); }); }); diff --git a/src/utils/deprecated.js b/src/utils/deprecated.js index d5fd87c7be4be..fe696e050cafd 100644 --- a/src/utils/deprecated.js +++ b/src/utils/deprecated.js @@ -35,7 +35,9 @@ function deprecated(namespace, oldName, newName, ctx, fn) { return fn.apply(ctx, arguments); }; newFn.displayName = `${namespace}_${oldName}`; - return newFn; + // We need to make sure all properties of the original fn are copied over. + // In particular, this is needed to support PropTypes + return Object.assign(newFn, fn); } return fn;