diff --git a/README.md b/README.md
index 0b832e9..1a23c9d 100644
--- a/README.md
+++ b/README.md
@@ -78,9 +78,12 @@ MyComponent.propTypes = {
// (or fragment) containing these types.
optionalNode: PropTypes.node,
- // A React element.
+ // A React element (ie. ).
optionalElement: PropTypes.element,
+ // A React element type (ie. MyComponent).
+ optionalElementType: PropTypes.elementType,
+
// You can also declare that a prop is an instance of a class. This uses
// JS's instanceof operator.
optionalMessage: PropTypes.instanceOf(Message),
diff --git a/__tests__/PropTypesDevelopmentStandalone-test.js b/__tests__/PropTypesDevelopmentStandalone-test.js
index b2ffaa1..e65c1d3 100644
--- a/__tests__/PropTypesDevelopmentStandalone-test.js
+++ b/__tests__/PropTypesDevelopmentStandalone-test.js
@@ -18,6 +18,7 @@ function resetWarningCache() {
React = require('react');
PropTypes = require('../index');
}
+resetWarningCache();
function getPropTypeWarningMessage(propTypes, object, componentName) {
if (!console.error.calls) {
@@ -555,6 +556,77 @@ describe('PropTypesDevelopmentStandalone', () => {
});
});
+ describe('ElementType Types', () => {
+ it('should support native component', () => {
+ typeCheckPass(PropTypes.elementType, 'div');
+ });
+
+ it('should support stateless component', () => {
+ var MyComponent = () =>
;
+ typeCheckPass(PropTypes.elementType, MyComponent);
+ });
+
+ it('should support stateful component', () => {
+ class MyComponent extends React.Component {
+ render() {
+ return ;
+ }
+ }
+ typeCheckPass(PropTypes.elementType, MyComponent);
+ });
+
+ (React.forwardRef ? it : it.skip)('should support forwardRef component', () => {
+ const MyForwardRef = React.forwardRef((props, ref) => );
+ typeCheckPass(PropTypes.elementType, MyForwardRef);
+ });
+
+ (React.createContext ? it : it.skip)('should support context provider component', () => {
+ const MyContext = React.createContext('test');
+ typeCheckPass(PropTypes.elementType, MyContext.Provider);
+ });
+
+ (React.createContext ? it : it.skip)('should support context consumer component', () => {
+ const MyContext = React.createContext('test');
+ typeCheckPass(PropTypes.elementType, MyContext.Consumer);
+ });
+
+ it('should warn for invalid types', () => {
+ typeCheckFail(
+ PropTypes.elementType,
+ true,
+ 'Invalid prop `testProp` of type `boolean` supplied to ' +
+ '`testComponent`, expected a single ReactElement type.',
+ );
+
+ typeCheckFail(
+ PropTypes.elementType,
+ {},
+ 'Invalid prop `testProp` of type `object` supplied to ' +
+ '`testComponent`, expected a single ReactElement type.',
+ );
+
+ typeCheckFail(
+ PropTypes.elementType,
+ [],
+ 'Invalid prop `testProp` of type `array` supplied to ' +
+ '`testComponent`, expected a single ReactElement type.',
+ );
+
+ it('should warn for missing required values', () => {
+ typeCheckFailRequiredValues(PropTypes.elementType.isRequired);
+ });
+ });
+
+ it('should warn for React element', () => {
+ typeCheckFail(
+ PropTypes.elementType,
+ ,
+ 'Invalid prop `testProp` of type `object` supplied to ' +
+ '`testComponent`, expected a single ReactElement type.',
+ );
+ });
+ });
+
describe('Instance Types', () => {
it('should warn for invalid instances', () => {
function Person() {}
diff --git a/__tests__/PropTypesProductionStandalone-test.js b/__tests__/PropTypesProductionStandalone-test.js
index 3b2693c..1e2ca0b 100644
--- a/__tests__/PropTypesProductionStandalone-test.js
+++ b/__tests__/PropTypesProductionStandalone-test.js
@@ -162,6 +162,13 @@ describe('PropTypesProductionStandalone', function() {
});
});
+ describe('React ElementType Type', function() {
+ it('shoud be a no-op', function() {
+ expectThrowsInProduction(PropTypes.elementType.isRequired, false);
+ expectThrowsInProduction(PropTypes.elementType.isRequired, {});
+ });
+ });
+
describe('ObjectOf Type', function() {
it('should be a no-op', function() {
expectThrowsInProduction(PropTypes.objectOf({foo: PropTypes.string}), {
diff --git a/factoryWithThrowingShims.js b/factoryWithThrowingShims.js
index e0f8062..e5b2f9c 100644
--- a/factoryWithThrowingShims.js
+++ b/factoryWithThrowingShims.js
@@ -45,6 +45,7 @@ module.exports = function() {
any: shim,
arrayOf: getShim,
element: shim,
+ elementType: shim,
instanceOf: getShim,
node: shim,
objectOf: getShim,
diff --git a/factoryWithTypeCheckers.js b/factoryWithTypeCheckers.js
index 1b73c3b..1a5f759 100644
--- a/factoryWithTypeCheckers.js
+++ b/factoryWithTypeCheckers.js
@@ -7,6 +7,7 @@
'use strict';
+var ReactIs = require('react-is');
var assign = require('object-assign');
var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
@@ -123,6 +124,7 @@ module.exports = function(isValidElement, throwOnDirectAccess) {
any: createAnyTypeChecker(),
arrayOf: createArrayOfTypeChecker,
element: createElementTypeChecker(),
+ elementType: createElementTypeTypeChecker(),
instanceOf: createInstanceTypeChecker,
node: createNodeChecker(),
objectOf: createObjectOfTypeChecker,
@@ -276,6 +278,18 @@ module.exports = function(isValidElement, throwOnDirectAccess) {
return createChainableTypeChecker(validate);
}
+ function createElementTypeTypeChecker() {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ if (!ReactIs.isValidElementType(propValue)) {
+ var propType = getPropType(propValue);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement type.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
function createInstanceTypeChecker(expectedClass) {
function validate(props, propName, componentName, location, propFullName) {
if (!(props[propName] instanceof expectedClass)) {
diff --git a/index.js b/index.js
index 11bfcea..e9ef51d 100644
--- a/index.js
+++ b/index.js
@@ -6,21 +6,12 @@
*/
if (process.env.NODE_ENV !== 'production') {
- var REACT_ELEMENT_TYPE = (typeof Symbol === 'function' &&
- Symbol.for &&
- Symbol.for('react.element')) ||
- 0xeac7;
-
- var isValidElement = function(object) {
- return typeof object === 'object' &&
- object !== null &&
- object.$$typeof === REACT_ELEMENT_TYPE;
- };
+ var ReactIs = require('react-is');
// By explicitly using `prop-types` you are opting into new development behavior.
// http://fb.me/prop-types-in-prod
var throwOnDirectAccess = true;
- module.exports = require('./factoryWithTypeCheckers')(isValidElement, throwOnDirectAccess);
+ module.exports = require('./factoryWithTypeCheckers')(ReactIs.isElement, throwOnDirectAccess);
} else {
// By explicitly using `prop-types` you are opting into new production behavior.
// http://fb.me/prop-types-in-prod
diff --git a/package.json b/package.json
index 0f47058..98e15ab 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,8 @@
},
"homepage": "https://facebook.github.io/react/",
"dependencies": {
- "object-assign": "^4.1.1"
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
},
"scripts": {
"test": "jest",
diff --git a/yarn.lock b/yarn.lock
index 67d038a..b32117b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2356,6 +2356,11 @@ randombytes@^2.0.0, randombytes@^2.0.1:
dependencies:
safe-buffer "^5.1.0"
+react-is@^16.8.1:
+ version "16.8.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.1.tgz#a80141e246eb894824fb4f2901c0c50ef31d4cdb"
+ integrity sha512-ioMCzVDWvCvKD8eeT+iukyWrBGrA3DiFYkXfBsVYIRdaREZuBjENG+KjrikavCLasozqRWTwFUagU/O4vPpRMA==
+
react@^15.5.1:
version "15.6.2"
resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"