├── .babelrc ├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── __tests__ ├── PropTypesDevelopmentReact15.js ├── PropTypesDevelopmentStandalone-test.js ├── PropTypesProductionReact15-test.js └── PropTypesProductionStandalone-test.js ├── checkPropTypes.js ├── factory.js ├── factoryWithThrowingShims.js ├── factoryWithTypeCheckers.js ├── index.js ├── lib ├── ReactPropTypesSecret.js └── has.js ├── package.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | prop-types.min.js 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parserOptions": { 4 | "ecmaVersion": 5, 5 | }, 6 | "env": { 7 | "browser": true, 8 | "node": true, 9 | }, 10 | "rules": { 11 | "no-console": "off", 12 | "no-multi-spaces": ["error"], 13 | "key-spacing": ["error"], 14 | }, 15 | "overrides": [ 16 | { 17 | "files": "**/__tests__/**/*", 18 | "env": { 19 | "jest": true, 20 | "jasmine": true, 21 | "es6": true, 22 | }, 23 | "rules": { 24 | "no-unused-vars": "off", 25 | }, 26 | "parserOptions": { 27 | "ecmaVersion": 2019, 28 | "ecmaFeatures": { 29 | "jsx": true, 30 | }, 31 | }, 32 | }, 33 | ], 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | 6 | permissions: 7 | contents: read # to fetch code (actions/checkout) 8 | 9 | jobs: 10 | ci: 11 | name: "Build & Test (React v${{ matrix.react_version}})" 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | react_version: 17 | - "17" 18 | - "16" 19 | - "16.0" 20 | - "15" 21 | - "15.0" 22 | - "0.14.9" 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions/setup-node@v2 26 | with: 27 | node-version: "lts/*" 28 | cache: 'yarn' 29 | - run: yarn install 30 | - run: yarn add react@${{ matrix.react_version }} 31 | - run: yarn list --pattern 'react' 32 | - run: yarn run pretest 33 | - run: yarn run tests-only 34 | - run: yarn run build 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | prop-types.js 3 | prop-types.min.js -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebook/prop-types/1c9c6311c1fb8778bffc7a1ca70c273ee8a8654d/.npmignore -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 15.8.1 2 | * [Fix] fix crash when a custom propType return lacks `.data`; call `hasOwnProperty` properly (#370) 3 | * [meta] Fix formatting in CHANGELOG.md (#367) 4 | * [Tests] add missing test coverage (#370) 5 | * [Tests] convert normal `it` functions to arrow functions (#370) 6 | * [Tests] do not fail fast; add react 17 (#366) 7 | * [Dev Deps] update `eslint` 8 | 9 | ## 15.8.0 10 | * [New] add `PropTypes.bigint` (#365) 11 | * [New] `oneOfType`: Add expected types to warning (#198) 12 | * [New] Add type check for validator for 'shape' and 'exact' (#234) 13 | * [Fix] `checkPropTypes`: Friendlier message when using a type checker that is not a function (#51) 14 | * [Refactor] extract `has` (#261, #125, #124) 15 | * [readme] Fix branch name (master -> main) (#364) 16 | * [readme] Clarify usage of `elementType` (#335) 17 | * [docs] highlighted the func name (#321) 18 | * [docs] Typo fix in example (#300) 19 | * [docs] Add instructions for intentional inclusion of validation in production. (#262) 20 | * [docs] `PropTypes.node`: add link to react docs 21 | * [docs] Improve wording for `checkPropTypes` (#258) 22 | * [meta] Add a package `sideEffects` field. (#350) 23 | * [meta] use `in-publish` to avoid running the build on install 24 | * [deps] regenerate yarn.lock 25 | * [deps] update `react-is` (#347, #346, #345, #340, #338) 26 | * [eslint] enable some rules (#360) 27 | * [Tests] Use GH Actions (#363) 28 | * [Tests] Fix spelling (#318) 29 | * [Tests] Fixed typo: 'Any type *should* accept any value' (#281) 30 | * [Tests] fix broken tests; test the build process 31 | * [Dev Deps] update `browserify`, `bundle-collapser`, `eslint`, `in-publish`, `react`, `uglifyify`, `uglifyjs` 32 | 33 | ## 15.7.2 34 | * [Fix] ensure nullish values in `oneOf` do not crash ([#256](https://github.com/facebook/prop-types/issues/256)) 35 | * [Fix] move `loose-envify` back to production deps, for browerify usage ([#203](https://github.com/facebook/prop-types/issues/203)) 36 | 37 | ## 15.7.1 38 | * [Fix] avoid template literal syntax ([#255](https://github.com/facebook/prop-types/issues/255), [#254](https://github.com/facebook/prop-types/issues/254)) 39 | 40 | ## 15.7.0 41 | * [New] Add `.elementType` ([#211](https://github.com/facebook/prop-types/pull/211)) 42 | * [New] add `PropTypes.resetWarningCache` ([#178](https://github.com/facebook/prop-types/pull/178)) 43 | * `oneOf`: improve warning when multiple arguments are supplied ([#244](https://github.com/facebook/prop-types/pull/244)) 44 | * Fix `oneOf` when used with Symbols ([#224](https://github.com/facebook/prop-types/pull/224)) 45 | * Avoid relying on `hasOwnProperty` being present on values' prototypes ([#112](https://github.com/facebook/prop-types/pull/112), [#187](https://github.com/facebook/prop-types/pull/187)) 46 | * Improve readme ([#248](https://github.com/facebook/prop-types/pull/248), [#233](https://github.com/facebook/prop-types/pull/233)) 47 | * Clean up mistaken runtime dep, swap envify for loose-envify ([#204](https://github.com/facebook/prop-types/pull/204)) 48 | 49 | ## 15.6.2 50 | * Remove the `fbjs` dependency by inlining some helpers from it ([#194](https://github.com/facebook/prop-types/pull/194))) 51 | 52 | ## 15.6.1 53 | * Fix an issue where outdated BSD license headers were still present in the published bundle [#162](https://github.com/facebook/prop-types/issues/162) 54 | 55 | ## 15.6.0 56 | 57 | * Switch from BSD + Patents to MIT license 58 | * Add PropTypes.exact, like PropTypes.shape but warns on extra object keys. ([@thejameskyle](https://github.com/thejameskyle) and [@aweary](https://github.com/aweary) in [#41](https://github.com/facebook/prop-types/pull/41) and [#87](https://github.com/facebook/prop-types/pull/87)) 59 | 60 | ## 15.5.10 61 | 62 | * Fix a false positive warning when using a production UMD build of a third-party library with a DEV version of React. ([@gaearon](https://github.com/gaearon) in [#50](https://github.com/facebook/prop-types/pull/50)) 63 | 64 | ## 15.5.9 65 | 66 | * Add `loose-envify` Browserify transform for users who don't envify globally. ([@mridgway](https://github.com/mridgway) in [#45](https://github.com/facebook/prop-types/pull/45)) 67 | 68 | ## 15.5.8 69 | 70 | * Limit the manual PropTypes call warning count because it has false positives with React versions earlier than 15.2.0 in the 15.x branch and 0.14.9 in the 0.14.x branch. ([@gaearon](https://github.com/gaearon) in [#26](https://github.com/facebook/prop-types/pull/26)) 71 | 72 | ## 15.5.7 73 | 74 | * **Critical Bugfix:** Fix an accidental breaking change that caused errors in production when used through `React.PropTypes`. ([@gaearon](https://github.com/gaearon) in [#20](https://github.com/facebook/prop-types/pull/20)) 75 | * Improve the size of production UMD build. ([@aweary](https://github.com/aweary) in [38ba18](https://github.com/facebook/prop-types/commit/38ba18a4a8f705f4b2b33c88204573ddd604f2d6) and [7882a7](https://github.com/facebook/prop-types/commit/7882a7285293db5f284bcf559b869fd2cd4c44d4)) 76 | 77 | ## 15.5.6 78 | 79 | **Note: this release has a critical issue and was deprecated. Please update to 15.5.7 or higher.** 80 | 81 | * Fix a markdown issue in README. ([@bvaughn](https://github.com/bvaughn) in [174f77](https://github.com/facebook/prop-types/commit/174f77a50484fa628593e84b871fb40eed78b69a)) 82 | 83 | ## 15.5.5 84 | 85 | **Note: this release has a critical issue and was deprecated. Please update to 15.5.7 or higher.** 86 | 87 | * Add missing documentation and license files. ([@bvaughn](https://github.com/bvaughn) in [0a53d3](https://github.com/facebook/prop-types/commit/0a53d3a34283ae1e2d3aa396632b6dc2a2061e6a)) 88 | 89 | ## 15.5.4 90 | 91 | **Note: this release has a critical issue and was deprecated. Please update to 15.5.7 or higher.** 92 | 93 | * Reduce the size of the UMD Build. ([@acdlite](https://github.com/acdlite) in [31e9344](https://github.com/facebook/prop-types/commit/31e9344ca3233159928da66295da17dad82db1a8)) 94 | * Remove bad package url. ([@ljharb](https://github.com/ljharb) in [158198f](https://github.com/facebook/prop-types/commit/158198fd6c468a3f6f742e0e355e622b3914048a)) 95 | * Remove the accidentally included typechecking code from the production build. 96 | 97 | ## 15.5.3 98 | 99 | **Note: this release has a critical issue and was deprecated. Please update to 15.5.7 or higher.** 100 | 101 | * Remove the accidentally included React package code from the UMD bundle. ([@acdlite](https://github.com/acdlite) in [df318bb](https://github.com/facebook/prop-types/commit/df318bba8a89bc5aadbb0292822cf4ed71d27ace)) 102 | 103 | ## 15.5.2 104 | 105 | **Note: this release has a critical issue and was deprecated. Please update to 15.5.7 or higher.** 106 | 107 | * Remove dependency on React for CommonJS entry point. ([@acdlite](https://github.com/acdlite) in [cae72bb](https://github.com/facebook/prop-types/commit/cae72bb281a3766c765e3624f6088c3713567e6d)) 108 | 109 | 110 | ## 15.5.1 111 | 112 | **Note: this release has a critical issue and was deprecated. Please update to 15.5.7 or higher.** 113 | 114 | * Remove accidental uncompiled ES6 syntax in the published package. ([@acdlite](https://github.com/acdlite) in [e191963](https://github.com/facebook/react/commit/e1919638b39dd65eedd250a8bb649773ca61b6f1)) 115 | 116 | ## 15.5.0 117 | 118 | **Note: this release has a critical issue and was deprecated. Please update to 15.5.7 or higher.** 119 | 120 | * Initial release. 121 | 122 | ## Before 15.5.0 123 | 124 | PropTypes was previously included in React, but is now a separate package. For earlier history of PropTypes [see the React change log.](https://github.com/facebook/react/blob/HEAD/CHANGELOG.md) 125 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated. 4 | 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to prop-types 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Code of Conduct 6 | The code of conduct is described in [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md). 7 | 8 | ## Pull Requests 9 | We actively welcome your pull requests. 10 | 11 | 1. Fork the repo and create your branch from `main`. 12 | 2. If you've added code that should be tested, add tests. 13 | 3. If you've changed APIs, update the documentation. 14 | 4. Ensure the test suite passes. 15 | 5. Make sure your code lints. 16 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 17 | 18 | ## Contributor License Agreement ("CLA") 19 | In order to accept your pull request, we need you to submit a CLA. You only need 20 | to do this once to work on any of Facebook's open source projects. 21 | 22 | Complete your CLA here: 23 | 24 | ## Issues 25 | We use GitHub issues to track public bugs. Please ensure your description is 26 | clear and has sufficient instructions to be able to reproduce the issue. 27 | 28 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 29 | disclosure of security bugs. In those cases, please go through the process 30 | outlined on that page and do not file a public issue. 31 | 32 | ## License 33 | By contributing to prop-types, you agree that your contributions will be licensed 34 | under the LICENSE file in the root directory of this source tree. 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013-present, Facebook, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # prop-types [![Support Ukraine](https://img.shields.io/badge/Support-Ukraine-FFD500?style=flat&labelColor=005BBB)](https://opensource.fb.com/support-ukraine) [![Build Status](https://travis-ci.com/facebook/prop-types.svg?branch=main)](https://travis-ci.org/facebook/prop-types) 2 | 3 | Runtime type checking for React props and similar objects. 4 | 5 | You can use prop-types to document the intended types of properties passed to 6 | components. React (and potentially other libraries—see the `checkPropTypes()` 7 | reference below) will check props passed to your components against those 8 | definitions, and warn in development if they don’t match. 9 | 10 | ## Installation 11 | 12 | ```shell 13 | npm install --save prop-types 14 | ``` 15 | 16 | ## Importing 17 | 18 | ```js 19 | import PropTypes from 'prop-types'; // ES6 20 | var PropTypes = require('prop-types'); // ES5 with npm 21 | ``` 22 | 23 | ### CDN 24 | 25 | If you prefer to exclude `prop-types` from your application and use it 26 | globally via `window.PropTypes`, the `prop-types` package provides 27 | single-file distributions, which are hosted on the following CDNs: 28 | 29 | * [**unpkg**](https://unpkg.com/prop-types/) 30 | ```html 31 | 32 | 33 | 34 | 35 | 36 | ``` 37 | 38 | * [**cdnjs**](https://cdnjs.com/libraries/prop-types) 39 | ```html 40 | 41 | 42 | 43 | 44 | 45 | ``` 46 | 47 | To load a specific version of `prop-types` replace `15.6.0` with the version number. 48 | 49 | ## Usage 50 | 51 | PropTypes was originally exposed as part of the React core module, and is 52 | commonly used with React components. 53 | Here is an example of using PropTypes with a React component, which also 54 | documents the different validators provided: 55 | 56 | ```js 57 | import React from 'react'; 58 | import PropTypes from 'prop-types'; 59 | 60 | class MyComponent extends React.Component { 61 | render() { 62 | // ... do things with the props 63 | } 64 | } 65 | 66 | MyComponent.propTypes = { 67 | // You can declare that a prop is a specific JS primitive. By default, these 68 | // are all optional. 69 | optionalArray: PropTypes.array, 70 | optionalBigInt: PropTypes.bigint, 71 | optionalBool: PropTypes.bool, 72 | optionalFunc: PropTypes.func, 73 | optionalNumber: PropTypes.number, 74 | optionalObject: PropTypes.object, 75 | optionalString: PropTypes.string, 76 | optionalSymbol: PropTypes.symbol, 77 | 78 | // Anything that can be rendered: numbers, strings, elements or an array 79 | // (or fragment) containing these types. 80 | // see https://reactjs.org/docs/rendering-elements.html for more info 81 | optionalNode: PropTypes.node, 82 | 83 | // A React element (ie. ). 84 | optionalElement: PropTypes.element, 85 | 86 | // A React element type (eg. MyComponent). 87 | // a function, string, or "element-like" object (eg. React.Fragment, Suspense, etc.) 88 | // see https://github.com/facebook/react/blob/HEAD/packages/shared/isValidElementType.js 89 | optionalElementType: PropTypes.elementType, 90 | 91 | // You can also declare that a prop is an instance of a class. This uses 92 | // JS's instanceof operator. 93 | optionalMessage: PropTypes.instanceOf(Message), 94 | 95 | // You can ensure that your prop is limited to specific values by treating 96 | // it as an enum. 97 | optionalEnum: PropTypes.oneOf(['News', 'Photos']), 98 | 99 | // An object that could be one of many types 100 | optionalUnion: PropTypes.oneOfType([ 101 | PropTypes.string, 102 | PropTypes.number, 103 | PropTypes.instanceOf(Message) 104 | ]), 105 | 106 | // An array of a certain type 107 | optionalArrayOf: PropTypes.arrayOf(PropTypes.number), 108 | 109 | // An object with property values of a certain type 110 | optionalObjectOf: PropTypes.objectOf(PropTypes.number), 111 | 112 | // You can chain any of the above with `isRequired` to make sure a warning 113 | // is shown if the prop isn't provided. 114 | 115 | // An object taking on a particular shape 116 | optionalObjectWithShape: PropTypes.shape({ 117 | optionalProperty: PropTypes.string, 118 | requiredProperty: PropTypes.number.isRequired 119 | }), 120 | 121 | // An object with warnings on extra properties 122 | optionalObjectWithStrictShape: PropTypes.exact({ 123 | optionalProperty: PropTypes.string, 124 | requiredProperty: PropTypes.number.isRequired 125 | }), 126 | 127 | requiredFunc: PropTypes.func.isRequired, 128 | 129 | // A value of any data type 130 | requiredAny: PropTypes.any.isRequired, 131 | 132 | // You can also specify a custom validator. It should return an Error 133 | // object if the validation fails. Don't `console.warn` or throw, as this 134 | // won't work inside `oneOfType`. 135 | customProp: function(props, propName, componentName) { 136 | if (!/matchme/.test(props[propName])) { 137 | return new Error( 138 | 'Invalid prop `' + propName + '` supplied to' + 139 | ' `' + componentName + '`. Validation failed.' 140 | ); 141 | } 142 | }, 143 | 144 | // You can also supply a custom validator to `arrayOf` and `objectOf`. 145 | // It should return an Error object if the validation fails. The validator 146 | // will be called for each key in the array or object. The first two 147 | // arguments of the validator are the array or object itself, and the 148 | // current item's key. 149 | customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) { 150 | if (!/matchme/.test(propValue[key])) { 151 | return new Error( 152 | 'Invalid prop `' + propFullName + '` supplied to' + 153 | ' `' + componentName + '`. Validation failed.' 154 | ); 155 | } 156 | }) 157 | }; 158 | ``` 159 | 160 | Refer to the [React documentation](https://facebook.github.io/react/docs/typechecking-with-proptypes.html) for more information. 161 | 162 | ## Migrating from React.PropTypes 163 | 164 | Check out [Migrating from React.PropTypes](https://facebook.github.io/react/blog/2017/04/07/react-v15.5.0.html#migrating-from-react.proptypes) for details on how to migrate to `prop-types` from `React.PropTypes`. 165 | 166 | Note that this blog posts **mentions a codemod script that performs the conversion automatically**. 167 | 168 | There are also important notes below. 169 | 170 | ## How to Depend on This Package? 171 | 172 | For apps, we recommend putting it in `dependencies` with a caret range. 173 | For example: 174 | 175 | ```js 176 | "dependencies": { 177 | "prop-types": "^15.5.7" 178 | } 179 | ``` 180 | 181 | For libraries, we *also* recommend leaving it in `dependencies`: 182 | 183 | ```js 184 | "dependencies": { 185 | "prop-types": "^15.5.7" 186 | }, 187 | "peerDependencies": { 188 | "react": "^15.5.0" 189 | } 190 | ``` 191 | 192 | **Note:** there are known issues in versions before 15.5.7 so we recommend using it as the minimal version. 193 | 194 | Make sure that the version range uses a caret (`^`) and thus is broad enough for npm to efficiently deduplicate packages. 195 | 196 | For UMD bundles of your components, make sure you **don’t** include `PropTypes` in the build. Usually this is done by marking it as an external (the specifics depend on your bundler), just like you do with React. 197 | 198 | ## Compatibility 199 | 200 | ### React 0.14 201 | 202 | This package is compatible with **React 0.14.9**. Compared to 0.14.8 (which was released in March of 2016), there are no other changes in 0.14.9, so it should be a painless upgrade. 203 | 204 | ```shell 205 | # ATTENTION: Only run this if you still use React 0.14! 206 | npm install --save react@^0.14.9 react-dom@^0.14.9 207 | ``` 208 | 209 | ### React 15+ 210 | 211 | This package is compatible with **React 15.3.0** and higher. 212 | 213 | ``` 214 | npm install --save react@^15.3.0 react-dom@^15.3.0 215 | ``` 216 | 217 | ### What happens on other React versions? 218 | 219 | It outputs warnings with the message below even though the developer doesn’t do anything wrong. Unfortunately there is no solution for this other than updating React to either 15.3.0 or higher, or 0.14.9 if you’re using React 0.14. 220 | 221 | ## Difference from `React.PropTypes`: Don’t Call Validator Functions 222 | 223 | First of all, **which version of React are you using**? You might be seeing this message because a component library has updated to use `prop-types` package, but your version of React is incompatible with it. See the [above section](#compatibility) for more details. 224 | 225 | Are you using either React 0.14.9 or a version higher than React 15.3.0? Read on. 226 | 227 | When you migrate components to use the standalone `prop-types`, **all validator functions will start throwing an error if you call them directly**. This makes sure that nobody relies on them in production code, and it is safe to strip their implementations to optimize the bundle size. 228 | 229 | Code like this is still fine: 230 | 231 | ```js 232 | MyComponent.propTypes = { 233 | myProp: PropTypes.bool 234 | }; 235 | ``` 236 | 237 | However, code like this will not work with the `prop-types` package: 238 | 239 | ```js 240 | // Will not work with `prop-types` package! 241 | var errorOrNull = PropTypes.bool(42, 'myProp', 'MyComponent', 'prop'); 242 | ``` 243 | 244 | It will throw an error: 245 | 246 | ``` 247 | Calling PropTypes validators directly is not supported by the `prop-types` package. 248 | Use PropTypes.checkPropTypes() to call them. 249 | ``` 250 | 251 | (If you see **a warning** rather than an error with this message, please check the [above section about compatibility](#compatibility).) 252 | 253 | This is new behavior, and you will only encounter it when you migrate from `React.PropTypes` to the `prop-types` package. For the vast majority of components, this doesn’t matter, and if you didn’t see [this warning](https://facebook.github.io/react/warnings/dont-call-proptypes.html) in your components, your code is safe to migrate. This is not a breaking change in React because you are only opting into this change for a component by explicitly changing your imports to use `prop-types`. If you temporarily need the old behavior, you can keep using `React.PropTypes` until React 16. 254 | 255 | **If you absolutely need to trigger the validation manually**, call `PropTypes.checkPropTypes()`. Unlike the validators themselves, this function is safe to call in production, as it will be replaced by an empty function: 256 | 257 | ```js 258 | // Works with standalone PropTypes 259 | PropTypes.checkPropTypes(MyComponent.propTypes, props, 'prop', 'MyComponent'); 260 | ``` 261 | See below for more info. 262 | 263 | **If you DO want to use validation in production**, you can choose to use the **development version** by importing/requiring `prop-types/prop-types` instead of `prop-types`. 264 | 265 | **You might also see this error** if you’re calling a `PropTypes` validator from your own custom `PropTypes` validator. In this case, the fix is to make sure that you are passing *all* of the arguments to the inner function. There is a more in-depth explanation of how to fix it [on this page](https://facebook.github.io/react/warnings/dont-call-proptypes.html#fixing-the-false-positive-in-third-party-proptypes). Alternatively, you can temporarily keep using `React.PropTypes` until React 16, as it would still only warn in this case. 266 | 267 | If you use a bundler like Browserify or Webpack, don’t forget to [follow these instructions](https://reactjs.org/docs/optimizing-performance.html#use-the-production-build) to correctly bundle your application in development or production mode. Otherwise you’ll ship unnecessary code to your users. 268 | 269 | ## PropTypes.checkPropTypes 270 | 271 | React will automatically check the propTypes you set on the component, but if 272 | you are using PropTypes without React then you may want to manually call 273 | `PropTypes.checkPropTypes`, like so: 274 | 275 | ```js 276 | const myPropTypes = { 277 | name: PropTypes.string, 278 | age: PropTypes.number, 279 | // ... define your prop validations 280 | }; 281 | 282 | const props = { 283 | name: 'hello', // is valid 284 | age: 'world', // not valid 285 | }; 286 | 287 | // Let's say your component is called 'MyComponent' 288 | 289 | // Works with standalone PropTypes 290 | PropTypes.checkPropTypes(myPropTypes, props, 'prop', 'MyComponent'); 291 | // This will warn as follows: 292 | // Warning: Failed prop type: Invalid prop `age` of type `string` supplied to 293 | // `MyComponent`, expected `number`. 294 | ``` 295 | 296 | ## PropTypes.resetWarningCache() 297 | 298 | `PropTypes.checkPropTypes(...)` only `console.error`s a given message once. To reset the error warning cache in tests, call `PropTypes.resetWarningCache()` 299 | 300 | ### License 301 | 302 | prop-types is [MIT licensed](./LICENSE). 303 | -------------------------------------------------------------------------------- /__tests__/PropTypesDevelopmentReact15.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @emails react-core 8 | */ 9 | 10 | 'use strict'; 11 | 12 | let React; 13 | let PropTypes; 14 | 15 | function resetWarningCache() { 16 | jest.resetModules(); 17 | 18 | React = require('react'); 19 | // This is how React 15 imports `prop-types`. 20 | PropTypes = require('../factory')(React.isValidElement); 21 | } 22 | 23 | function getPropTypeWarningMessage(propTypes, object, componentName) { 24 | if (!console.error.calls) { 25 | spyOn(console, 'error'); 26 | } else { 27 | console.error.calls.reset(); 28 | } 29 | resetWarningCache(); 30 | 31 | PropTypes.checkPropTypes(propTypes, object, 'prop', componentName); 32 | const callCount = console.error.calls.count(); 33 | if (callCount > 1) { 34 | throw new Error('Too many warnings.'); 35 | } 36 | const message = console.error.calls.argsFor(0)[0] || null; 37 | console.error.calls.reset(); 38 | 39 | return message; 40 | } 41 | 42 | function typeCheckFail(declaration, value, expectedMessage) { 43 | const propTypes = { 44 | testProp: declaration, 45 | }; 46 | const props = { 47 | testProp: value, 48 | }; 49 | const message = getPropTypeWarningMessage(propTypes, props, 'testComponent'); 50 | expect(message).toContain(expectedMessage); 51 | } 52 | 53 | function typeCheckFailRequiredValues(declaration) { 54 | const specifiedButIsNullMsg = 'The prop `testProp` is marked as required in ' + 55 | '`testComponent`, but its value is `null`.'; 56 | const unspecifiedMsg = 'The prop `testProp` is marked as required in ' + 57 | '`testComponent`, but its value is \`undefined\`.'; 58 | 59 | const propTypes = {testProp: declaration}; 60 | 61 | // Required prop is null 62 | const message1 = getPropTypeWarningMessage( 63 | propTypes, 64 | {testProp: null}, 65 | 'testComponent', 66 | ); 67 | expect(message1).toContain(specifiedButIsNullMsg); 68 | 69 | // Required prop is undefined 70 | const message2 = getPropTypeWarningMessage( 71 | propTypes, 72 | {testProp: undefined}, 73 | 'testComponent', 74 | ); 75 | expect(message2).toContain(unspecifiedMsg); 76 | 77 | // Required prop is not a member of props object 78 | const message3 = getPropTypeWarningMessage(propTypes, {}, 'testComponent'); 79 | expect(message3).toContain(unspecifiedMsg); 80 | } 81 | 82 | function typeCheckPass(declaration, value) { 83 | const propTypes = { 84 | testProp: declaration, 85 | }; 86 | const props = { 87 | testProp: value, 88 | }; 89 | const message = getPropTypeWarningMessage(propTypes, props, 'testComponent'); 90 | expect(message).toBe(null); 91 | } 92 | 93 | function expectWarningInDevelopment(declaration, value) { 94 | resetWarningCache(); 95 | const props = {testProp: value}; 96 | const propName = 'testProp' + Math.random().toString(); 97 | const componentName = 'testComponent' + Math.random().toString(); 98 | for (let i = 0; i < 3; i++) { 99 | declaration(props, propName, componentName, 'prop'); 100 | } 101 | expect(console.error.calls.count()).toBe(1); 102 | expect(console.error.calls.argsFor(0)[0]).toContain( 103 | 'You are manually calling a React.PropTypes validation ', 104 | ); 105 | console.error.calls.reset(); 106 | } 107 | 108 | function expectInvalidValidatorWarning(declaration, type) { 109 | PropTypes.checkPropTypes({ foo: declaration }, { foo: {} }, 'prop', 'testComponent', null); 110 | expect(console.error.calls.argsFor(0)[0]).toEqual( 111 | 'Warning: Failed prop type: testComponent: prop type `foo.bar` is invalid; ' 112 | + 'it must be a function, usually from the `prop-types` package, but received `' + type + '`.' 113 | ); 114 | console.error.calls.reset(); 115 | } 116 | 117 | describe('PropTypesDevelopmentReact15', () => { 118 | beforeEach(() => { 119 | resetWarningCache(); 120 | }); 121 | 122 | describe('checkPropTypes', () => { 123 | it('should warn for invalid validators', () => { 124 | spyOn(console, 'error'); 125 | const propTypes = { foo: undefined }; 126 | const props = { foo: 'foo' }; 127 | PropTypes.checkPropTypes( 128 | propTypes, 129 | props, 130 | 'prop', 131 | 'testComponent', 132 | null, 133 | ); 134 | expect(console.error.calls.argsFor(0)[0]).toEqual( 135 | 'Warning: Failed prop type: testComponent: prop type `foo` is invalid; ' + 136 | 'it must be a function, usually from the `prop-types` package, but received `undefined`.' + 137 | 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.' 138 | ); 139 | }); 140 | 141 | it('should only warn once for identical validator failures', () => { 142 | spyOn(console, 'error'); 143 | const propTypes = { foo: undefined }; 144 | const props = { foo: 'foo' }; 145 | PropTypes.checkPropTypes( 146 | propTypes, 147 | props, 148 | 'prop', 149 | 'testComponent', 150 | null, 151 | ); 152 | PropTypes.checkPropTypes( 153 | propTypes, 154 | props, 155 | 'prop', 156 | 'testComponent', 157 | null, 158 | ); 159 | expect(console.error.calls.count()).toEqual(1); 160 | }); 161 | 162 | describe('checkPropTypes.resetWarningCache', () => { 163 | it('should reset warning cache', () => { 164 | spyOn(console, 'error'); 165 | const propTypes = { foo: undefined }; 166 | const props = { foo: 'foo' }; 167 | PropTypes.checkPropTypes( 168 | propTypes, 169 | props, 170 | 'prop', 171 | 'testComponent', 172 | null, 173 | ); 174 | PropTypes.checkPropTypes.resetWarningCache(); 175 | PropTypes.checkPropTypes( 176 | propTypes, 177 | props, 178 | 'prop', 179 | 'testComponent', 180 | null, 181 | ); 182 | expect(console.error.calls.count()).toEqual(2); 183 | }); 184 | }); 185 | 186 | it('does not return a value from a validator', () => { 187 | spyOn(console, 'error'); 188 | const propTypes = { 189 | foo(props, propName, componentName) { 190 | return new Error('some error'); 191 | }, 192 | }; 193 | const props = {foo: 'foo'}; 194 | const returnValue = PropTypes.checkPropTypes( 195 | propTypes, 196 | props, 197 | 'prop', 198 | 'testComponent', 199 | null, 200 | ); 201 | expect(console.error.calls.argsFor(0)[0]).toContain('some error'); 202 | expect(returnValue).toBe(undefined); 203 | }); 204 | 205 | it('does not throw if validator throws', () => { 206 | spyOn(console, 'error'); 207 | const propTypes = { 208 | foo(props, propName, componentName) { 209 | throw new Error('some error'); 210 | }, 211 | }; 212 | const props = {foo: 'foo'}; 213 | const returnValue = PropTypes.checkPropTypes( 214 | propTypes, 215 | props, 216 | 'prop', 217 | 'testComponent', 218 | null, 219 | ); 220 | expect(console.error.calls.argsFor(0)[0]).toContain('some error'); 221 | expect(returnValue).toBe(undefined); 222 | }); 223 | 224 | it('warns if any of the propTypes is not a function', () => { 225 | spyOn(console, 'error'); 226 | const propTypes = { 227 | foo: PropTypes.invalid_type, 228 | }; 229 | const props = { foo: 'foo' }; 230 | const returnValue = PropTypes.checkPropTypes(propTypes, props, 'prop', 'testComponent', null); 231 | expect(console.error.calls.argsFor(0)[0]).toEqual( 232 | 'Warning: Failed prop type: testComponent: prop type `foo` is invalid; ' + 233 | 'it must be a function, usually from the `prop-types` package, but received `undefined`.' + 234 | 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.' 235 | ); 236 | expect(returnValue).toBe(undefined); 237 | }); 238 | 239 | it('should warn for invalid validators inside shape', () => { 240 | spyOn(console, 'error'); 241 | expectInvalidValidatorWarning(PropTypes.shape({ bar: PropTypes.invalid_type }), 'undefined'); 242 | expectInvalidValidatorWarning(PropTypes.shape({ bar: true }), 'boolean'); 243 | expectInvalidValidatorWarning(PropTypes.shape({ bar: 'true' }), 'string'); 244 | expectInvalidValidatorWarning(PropTypes.shape({ bar: null }), 'null'); 245 | }); 246 | 247 | it('should warn for invalid validators inside exact', () => { 248 | spyOn(console, 'error'); 249 | expectInvalidValidatorWarning(PropTypes.exact({ bar: PropTypes.invalid_type }), 'undefined'); 250 | expectInvalidValidatorWarning(PropTypes.exact({ bar: true }), 'boolean'); 251 | expectInvalidValidatorWarning(PropTypes.exact({ bar: 'true' }), 'string'); 252 | expectInvalidValidatorWarning(PropTypes.exact({ bar: null }), 'null'); 253 | }); 254 | }); 255 | 256 | describe('resetWarningCache', () => { 257 | it('should reset warning cache', () => { 258 | spyOn(console, 'error'); 259 | const propTypes = { foo: undefined }; 260 | const props = { foo: 'foo' }; 261 | PropTypes.checkPropTypes( 262 | propTypes, 263 | props, 264 | 'prop', 265 | 'testComponent', 266 | null, 267 | ); 268 | PropTypes.resetWarningCache(); 269 | PropTypes.checkPropTypes( 270 | propTypes, 271 | props, 272 | 'prop', 273 | 'testComponent', 274 | null, 275 | ); 276 | expect(console.error.calls.count()).toEqual(2); 277 | }); 278 | }); 279 | 280 | describe('Primitive Types', () => { 281 | it('should warn for invalid strings', () => { 282 | typeCheckFail( 283 | PropTypes.string, 284 | [], 285 | 'Invalid prop `testProp` of type `array` supplied to ' + 286 | '`testComponent`, expected `string`.', 287 | ); 288 | typeCheckFail( 289 | PropTypes.string, 290 | false, 291 | 'Invalid prop `testProp` of type `boolean` supplied to ' + 292 | '`testComponent`, expected `string`.', 293 | ); 294 | typeCheckFail( 295 | PropTypes.string, 296 | 0, 297 | 'Invalid prop `testProp` of type `number` supplied to ' + 298 | '`testComponent`, expected `string`.', 299 | ); 300 | typeCheckFail( 301 | PropTypes.string, 302 | {}, 303 | 'Invalid prop `testProp` of type `object` supplied to ' + 304 | '`testComponent`, expected `string`.', 305 | ); 306 | typeCheckFail( 307 | PropTypes.string, 308 | Symbol(), 309 | 'Invalid prop `testProp` of type `symbol` supplied to ' + 310 | '`testComponent`, expected `string`.', 311 | ); 312 | }); 313 | 314 | it('should fail date and regexp correctly', () => { 315 | typeCheckFail( 316 | PropTypes.string, 317 | new Date(), 318 | 'Invalid prop `testProp` of type `date` supplied to ' + 319 | '`testComponent`, expected `string`.', 320 | ); 321 | typeCheckFail( 322 | PropTypes.string, 323 | /please/, 324 | 'Invalid prop `testProp` of type `regexp` supplied to ' + 325 | '`testComponent`, expected `string`.', 326 | ); 327 | }); 328 | 329 | it('should not warn for valid values', () => { 330 | typeCheckPass(PropTypes.array, []); 331 | if (typeof BigInt === 'function') { 332 | typeCheckPass(PropTypes.bigint, BigInt(0)); 333 | } 334 | typeCheckPass(PropTypes.bool, false); 335 | typeCheckPass(PropTypes.func, function() {}); 336 | typeCheckPass(PropTypes.number, 0); 337 | typeCheckPass(PropTypes.string, ''); 338 | typeCheckPass(PropTypes.object, {}); 339 | typeCheckPass(PropTypes.object, new Date()); 340 | typeCheckPass(PropTypes.object, /please/); 341 | typeCheckPass(PropTypes.symbol, Symbol()); 342 | }); 343 | 344 | it('should be implicitly optional and not warn without values', () => { 345 | typeCheckPass(PropTypes.string, null); 346 | typeCheckPass(PropTypes.string, undefined); 347 | }); 348 | 349 | it('should warn for missing required values', () => { 350 | typeCheckFailRequiredValues(PropTypes.string.isRequired); 351 | typeCheckFailRequiredValues(PropTypes.array.isRequired); 352 | typeCheckFailRequiredValues(PropTypes.symbol.isRequired); 353 | typeCheckFailRequiredValues(PropTypes.number.isRequired); 354 | typeCheckFailRequiredValues(PropTypes.bigint.isRequired); 355 | typeCheckFailRequiredValues(PropTypes.bool.isRequired); 356 | typeCheckFailRequiredValues(PropTypes.func.isRequired); 357 | typeCheckFailRequiredValues(PropTypes.shape({}).isRequired); 358 | }); 359 | 360 | it('should warn if called manually in development', () => { 361 | spyOn(console, 'error'); 362 | expectWarningInDevelopment(PropTypes.array, /please/); 363 | expectWarningInDevelopment(PropTypes.array, []); 364 | expectWarningInDevelopment(PropTypes.array.isRequired, /please/); 365 | expectWarningInDevelopment(PropTypes.array.isRequired, []); 366 | expectWarningInDevelopment(PropTypes.array.isRequired, null); 367 | expectWarningInDevelopment(PropTypes.array.isRequired, undefined); 368 | expectWarningInDevelopment(PropTypes.bigint, function() {}); 369 | expectWarningInDevelopment(PropTypes.bigint, 42); 370 | if (typeof BigInt === 'function') { 371 | expectWarningInDevelopment(PropTypes.bigint, BigInt(42)); 372 | } 373 | expectWarningInDevelopment(PropTypes.bigint.isRequired, function() {}); 374 | expectWarningInDevelopment(PropTypes.bigint.isRequired, 42); 375 | expectWarningInDevelopment(PropTypes.bigint.isRequired, null); 376 | expectWarningInDevelopment(PropTypes.bigint.isRequired, undefined); 377 | expectWarningInDevelopment(PropTypes.bool, []); 378 | expectWarningInDevelopment(PropTypes.bool, true); 379 | expectWarningInDevelopment(PropTypes.bool.isRequired, []); 380 | expectWarningInDevelopment(PropTypes.bool.isRequired, true); 381 | expectWarningInDevelopment(PropTypes.bool.isRequired, null); 382 | expectWarningInDevelopment(PropTypes.bool.isRequired, undefined); 383 | expectWarningInDevelopment(PropTypes.func, false); 384 | expectWarningInDevelopment(PropTypes.func, function() {}); 385 | expectWarningInDevelopment(PropTypes.func.isRequired, false); 386 | expectWarningInDevelopment(PropTypes.func.isRequired, function() {}); 387 | expectWarningInDevelopment(PropTypes.func.isRequired, null); 388 | expectWarningInDevelopment(PropTypes.func.isRequired, undefined); 389 | expectWarningInDevelopment(PropTypes.number, function() {}); 390 | expectWarningInDevelopment(PropTypes.number, 42); 391 | expectWarningInDevelopment(PropTypes.number.isRequired, function() {}); 392 | expectWarningInDevelopment(PropTypes.number.isRequired, 42); 393 | expectWarningInDevelopment(PropTypes.number.isRequired, null); 394 | expectWarningInDevelopment(PropTypes.number.isRequired, undefined); 395 | expectWarningInDevelopment(PropTypes.string, 0); 396 | expectWarningInDevelopment(PropTypes.string, 'foo'); 397 | expectWarningInDevelopment(PropTypes.string.isRequired, 0); 398 | expectWarningInDevelopment(PropTypes.string.isRequired, 'foo'); 399 | expectWarningInDevelopment(PropTypes.string.isRequired, null); 400 | expectWarningInDevelopment(PropTypes.string.isRequired, undefined); 401 | expectWarningInDevelopment(PropTypes.symbol, 0); 402 | expectWarningInDevelopment(PropTypes.symbol, Symbol('Foo')); 403 | expectWarningInDevelopment(PropTypes.symbol.isRequired, 0); 404 | expectWarningInDevelopment(PropTypes.symbol.isRequired, Symbol('Foo')); 405 | expectWarningInDevelopment(PropTypes.symbol.isRequired, null); 406 | expectWarningInDevelopment(PropTypes.symbol.isRequired, undefined); 407 | expectWarningInDevelopment(PropTypes.object, ''); 408 | expectWarningInDevelopment(PropTypes.object, {foo: 'bar'}); 409 | expectWarningInDevelopment(PropTypes.object.isRequired, ''); 410 | expectWarningInDevelopment(PropTypes.object.isRequired, {foo: 'bar'}); 411 | expectWarningInDevelopment(PropTypes.object.isRequired, null); 412 | expectWarningInDevelopment(PropTypes.object.isRequired, undefined); 413 | }); 414 | }); 415 | 416 | describe('Any type', () => { 417 | it('should accept any value', () => { 418 | typeCheckPass(PropTypes.any, 0); 419 | typeCheckPass(PropTypes.any, 'str'); 420 | typeCheckPass(PropTypes.any, []); 421 | typeCheckPass(PropTypes.any, Symbol()); 422 | }); 423 | 424 | it('should be implicitly optional and not warn without values', () => { 425 | typeCheckPass(PropTypes.any, null); 426 | typeCheckPass(PropTypes.any, undefined); 427 | }); 428 | 429 | it('should warn for missing required values', () => { 430 | typeCheckFailRequiredValues(PropTypes.any.isRequired); 431 | }); 432 | 433 | it('should warn if called manually in development', () => { 434 | spyOn(console, 'error'); 435 | expectWarningInDevelopment(PropTypes.any, null); 436 | expectWarningInDevelopment(PropTypes.any.isRequired, null); 437 | expectWarningInDevelopment(PropTypes.any.isRequired, undefined); 438 | }); 439 | }); 440 | 441 | describe('ArrayOf Type', () => { 442 | it('should fail for invalid argument', () => { 443 | typeCheckFail( 444 | PropTypes.arrayOf({foo: PropTypes.string}), 445 | {foo: 'bar'}, 446 | 'Property `testProp` of component `testComponent` has invalid PropType notation inside arrayOf.', 447 | ); 448 | }); 449 | 450 | it('should support the arrayOf propTypes', () => { 451 | typeCheckPass(PropTypes.arrayOf(PropTypes.number), [1, 2, 3]); 452 | if (typeof BigInt === 'function') { 453 | typeCheckPass(PropTypes.arrayOf(PropTypes.bigint), [BigInt(1), BigInt(2), BigInt(3)]); 454 | } 455 | typeCheckPass(PropTypes.arrayOf(PropTypes.string), ['a', 'b', 'c']); 456 | typeCheckPass(PropTypes.arrayOf(PropTypes.oneOf(['a', 'b'])), ['a', 'b']); 457 | typeCheckPass(PropTypes.arrayOf(PropTypes.symbol), [Symbol(), Symbol()]); 458 | }); 459 | 460 | it('should support arrayOf with complex types', () => { 461 | typeCheckPass( 462 | PropTypes.arrayOf(PropTypes.shape({a: PropTypes.number.isRequired})), 463 | [{a: 1}, {a: 2}], 464 | ); 465 | 466 | function Thing() {} 467 | typeCheckPass(PropTypes.arrayOf(PropTypes.instanceOf(Thing)), [ 468 | new Thing(), 469 | new Thing(), 470 | ]); 471 | }); 472 | 473 | it('should warn with invalid items in the array', () => { 474 | typeCheckFail( 475 | PropTypes.arrayOf(PropTypes.number), 476 | [1, 2, 'b'], 477 | 'Invalid prop `testProp[2]` of type `string` supplied to ' + 478 | '`testComponent`, expected `number`.', 479 | ); 480 | }); 481 | 482 | it('should warn with invalid complex types', () => { 483 | function Thing() {} 484 | const name = Thing.name || '<>'; 485 | 486 | typeCheckFail( 487 | PropTypes.arrayOf(PropTypes.instanceOf(Thing)), 488 | [new Thing(), 'xyz'], 489 | 'Invalid prop `testProp[1]` of type `String` supplied to ' + 490 | '`testComponent`, expected instance of `' + 491 | name + 492 | '`.', 493 | ); 494 | }); 495 | 496 | it('should warn when passed something other than an array', () => { 497 | typeCheckFail( 498 | PropTypes.arrayOf(PropTypes.number), 499 | {'0': 'maybe-array', length: 1}, 500 | 'Invalid prop `testProp` of type `object` supplied to ' + 501 | '`testComponent`, expected an array.', 502 | ); 503 | typeCheckFail( 504 | PropTypes.arrayOf(PropTypes.number), 505 | 123, 506 | 'Invalid prop `testProp` of type `number` supplied to ' + 507 | '`testComponent`, expected an array.', 508 | ); 509 | typeCheckFail( 510 | PropTypes.arrayOf(PropTypes.number), 511 | 'string', 512 | 'Invalid prop `testProp` of type `string` supplied to ' + 513 | '`testComponent`, expected an array.', 514 | ); 515 | }); 516 | 517 | it('should not warn when passing an empty array', () => { 518 | typeCheckPass(PropTypes.arrayOf(PropTypes.number), []); 519 | }); 520 | 521 | it('should be implicitly optional and not warn without values', () => { 522 | typeCheckPass(PropTypes.arrayOf(PropTypes.number), null); 523 | typeCheckPass(PropTypes.arrayOf(PropTypes.number), undefined); 524 | }); 525 | 526 | it('should warn for missing required values', () => { 527 | typeCheckFailRequiredValues( 528 | PropTypes.arrayOf(PropTypes.number).isRequired, 529 | ); 530 | }); 531 | 532 | it('should warn if called manually in development', () => { 533 | spyOn(console, 'error'); 534 | expectWarningInDevelopment(PropTypes.arrayOf({foo: PropTypes.string}), { 535 | foo: 'bar', 536 | }); 537 | expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number), [ 538 | 1, 539 | 2, 540 | 'b', 541 | ]); 542 | expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number), { 543 | '0': 'maybe-array', 544 | length: 1, 545 | }); 546 | expectWarningInDevelopment( 547 | PropTypes.arrayOf(PropTypes.number).isRequired, 548 | null, 549 | ); 550 | expectWarningInDevelopment( 551 | PropTypes.arrayOf(PropTypes.number).isRequired, 552 | undefined, 553 | ); 554 | }); 555 | }); 556 | 557 | describe('Component Type', () => { 558 | it('should support components', () => { 559 | typeCheckPass(PropTypes.element,
); 560 | }); 561 | 562 | it('should not support multiple components or scalar values', () => { 563 | typeCheckFail( 564 | PropTypes.element, 565 | [
,
], 566 | 'Invalid prop `testProp` of type `array` supplied to `testComponent`, ' + 567 | 'expected a single ReactElement.', 568 | ); 569 | typeCheckFail( 570 | PropTypes.element, 571 | 123, 572 | 'Invalid prop `testProp` of type `number` supplied to `testComponent`, ' + 573 | 'expected a single ReactElement.', 574 | ); 575 | if (typeof BigInt === 'function') { 576 | typeCheckFail( 577 | PropTypes.element, 578 | BigInt(123), 579 | 'Invalid prop `testProp` of type `bigint` supplied to `testComponent`, ' + 580 | 'expected a single ReactElement.', 581 | ); 582 | } 583 | typeCheckFail( 584 | PropTypes.element, 585 | 'foo', 586 | 'Invalid prop `testProp` of type `string` supplied to `testComponent`, ' + 587 | 'expected a single ReactElement.', 588 | ); 589 | typeCheckFail( 590 | PropTypes.element, 591 | false, 592 | 'Invalid prop `testProp` of type `boolean` supplied to `testComponent`, ' + 593 | 'expected a single ReactElement.', 594 | ); 595 | }); 596 | 597 | it('should be implicitly optional and not warn without values', () => { 598 | typeCheckPass(PropTypes.element, null); 599 | typeCheckPass(PropTypes.element, undefined); 600 | }); 601 | 602 | it('should warn for missing required values', () => { 603 | typeCheckFailRequiredValues(PropTypes.element.isRequired); 604 | }); 605 | 606 | it('should warn if called manually in development', () => { 607 | spyOn(console, 'error'); 608 | expectWarningInDevelopment(PropTypes.element, [
,
]); 609 | expectWarningInDevelopment(PropTypes.element,
); 610 | expectWarningInDevelopment(PropTypes.element, 123); 611 | expectWarningInDevelopment(PropTypes.element, 'foo'); 612 | expectWarningInDevelopment(PropTypes.element, false); 613 | expectWarningInDevelopment(PropTypes.element.isRequired, null); 614 | expectWarningInDevelopment(PropTypes.element.isRequired, undefined); 615 | }); 616 | }); 617 | 618 | describe('Instance Types', () => { 619 | it('should warn for invalid instances', () => { 620 | function Person() {} 621 | function Cat() {} 622 | const personName = Person.name || '<>'; 623 | const dateName = Date.name || '<>'; 624 | const regExpName = RegExp.name || '<>'; 625 | 626 | typeCheckFail( 627 | PropTypes.instanceOf(Person), 628 | false, 629 | 'Invalid prop `testProp` of type `Boolean` supplied to ' + 630 | '`testComponent`, expected instance of `' + 631 | personName + 632 | '`.', 633 | ); 634 | typeCheckFail( 635 | PropTypes.instanceOf(Person), 636 | {}, 637 | 'Invalid prop `testProp` of type `Object` supplied to ' + 638 | '`testComponent`, expected instance of `' + 639 | personName + 640 | '`.', 641 | ); 642 | typeCheckFail( 643 | PropTypes.instanceOf(Person), 644 | '', 645 | 'Invalid prop `testProp` of type `String` supplied to ' + 646 | '`testComponent`, expected instance of `' + 647 | personName + 648 | '`.', 649 | ); 650 | typeCheckFail( 651 | PropTypes.instanceOf(Date), 652 | {}, 653 | 'Invalid prop `testProp` of type `Object` supplied to ' + 654 | '`testComponent`, expected instance of `' + 655 | dateName + 656 | '`.', 657 | ); 658 | typeCheckFail( 659 | PropTypes.instanceOf(RegExp), 660 | {}, 661 | 'Invalid prop `testProp` of type `Object` supplied to ' + 662 | '`testComponent`, expected instance of `' + 663 | regExpName + 664 | '`.', 665 | ); 666 | typeCheckFail( 667 | PropTypes.instanceOf(Person), 668 | new Cat(), 669 | 'Invalid prop `testProp` of type `Cat` supplied to ' + 670 | '`testComponent`, expected instance of `' + 671 | personName + 672 | '`.', 673 | ); 674 | typeCheckFail( 675 | PropTypes.instanceOf(Person), 676 | Object.create(null), 677 | 'Invalid prop `testProp` of type `<>` supplied to ' + 678 | '`testComponent`, expected instance of `' + 679 | personName + 680 | '`.', 681 | ); 682 | }); 683 | 684 | it('should not warn for valid values', () => { 685 | function Person() {} 686 | function Engineer() {} 687 | Engineer.prototype = new Person(); 688 | 689 | typeCheckPass(PropTypes.instanceOf(Person), new Person()); 690 | typeCheckPass(PropTypes.instanceOf(Person), new Engineer()); 691 | 692 | typeCheckPass(PropTypes.instanceOf(Date), new Date()); 693 | typeCheckPass(PropTypes.instanceOf(RegExp), /please/); 694 | }); 695 | 696 | it('should be implicitly optional and not warn without values', () => { 697 | typeCheckPass(PropTypes.instanceOf(String), null); 698 | typeCheckPass(PropTypes.instanceOf(String), undefined); 699 | }); 700 | 701 | it('should warn for missing required values', () => { 702 | typeCheckFailRequiredValues(PropTypes.instanceOf(String).isRequired); 703 | }); 704 | 705 | it('should warn if called manually in development', () => { 706 | spyOn(console, 'error'); 707 | expectWarningInDevelopment(PropTypes.instanceOf(Date), {}); 708 | expectWarningInDevelopment(PropTypes.instanceOf(Date), new Date()); 709 | expectWarningInDevelopment(PropTypes.instanceOf(Date).isRequired, {}); 710 | expectWarningInDevelopment( 711 | PropTypes.instanceOf(Date).isRequired, 712 | new Date(), 713 | ); 714 | }); 715 | }); 716 | 717 | describe('React Component Types', () => { 718 | it('should warn for invalid values', () => { 719 | const failMessage = 'Invalid prop `testProp` supplied to ' + 720 | '`testComponent`, expected a ReactNode.'; 721 | typeCheckFail(PropTypes.node, true, failMessage); 722 | typeCheckFail(PropTypes.node, function() {}, failMessage); 723 | typeCheckFail(PropTypes.node, {key: function() {}}, failMessage); 724 | typeCheckFail(PropTypes.node, {key:
}, failMessage); 725 | }); 726 | 727 | it('should not warn for valid values', () => { 728 | function MyComponent() {} 729 | MyComponent.prototype.render = function() { 730 | return
; 731 | }; 732 | typeCheckPass(PropTypes.node,
); 733 | typeCheckPass(PropTypes.node, false); 734 | typeCheckPass(PropTypes.node, ); 735 | typeCheckPass(PropTypes.node, 'Some string'); 736 | typeCheckPass(PropTypes.node, []); 737 | typeCheckPass(PropTypes.node, [ 738 | 123, 739 | 'Some string', 740 |
, 741 | ['Another string', [456], , ], 742 | , 743 | null, 744 | undefined, 745 | ]); 746 | }); 747 | 748 | it('should not warn for iterables', () => { 749 | function MyComponent() {} 750 | MyComponent.prototype.render = function() { 751 | return
; 752 | }; 753 | const iterable = { 754 | '@@iterator': function() { 755 | let i = 0; 756 | return { 757 | next: function() { 758 | const done = ++i > 2; 759 | return {value: done ? undefined : , done: done}; 760 | }, 761 | }; 762 | }, 763 | }; 764 | 765 | typeCheckPass(PropTypes.node, iterable); 766 | }); 767 | 768 | it('should not warn for entry iterables', () => { 769 | function MyComponent() {} 770 | MyComponent.prototype.render = function() { 771 | return
; 772 | }; 773 | const iterable = { 774 | '@@iterator': function() { 775 | let i = 0; 776 | return { 777 | next: function() { 778 | const done = ++i > 2; 779 | return { 780 | value: done ? undefined : ['#' + i, ], 781 | done: done, 782 | }; 783 | }, 784 | }; 785 | }, 786 | }; 787 | iterable.entries = iterable['@@iterator']; 788 | 789 | typeCheckPass(PropTypes.node, iterable); 790 | }); 791 | 792 | it('should not warn for null/undefined if not required', () => { 793 | typeCheckPass(PropTypes.node, null); 794 | typeCheckPass(PropTypes.node, undefined); 795 | }); 796 | 797 | it('should warn for missing required values', () => { 798 | typeCheckFailRequiredValues(PropTypes.node.isRequired); 799 | }); 800 | 801 | it('should accept empty array for required props', () => { 802 | typeCheckPass(PropTypes.node.isRequired, []); 803 | }); 804 | 805 | it('should warn if called manually in development', () => { 806 | spyOn(console, 'error'); 807 | expectWarningInDevelopment(PropTypes.node, 'node'); 808 | expectWarningInDevelopment(PropTypes.node, {}); 809 | expectWarningInDevelopment(PropTypes.node.isRequired, 'node'); 810 | expectWarningInDevelopment(PropTypes.node.isRequired, undefined); 811 | expectWarningInDevelopment(PropTypes.node.isRequired, undefined); 812 | }); 813 | }); 814 | 815 | describe('ObjectOf Type', () => { 816 | it('should fail for invalid argument', () => { 817 | typeCheckFail( 818 | PropTypes.objectOf({foo: PropTypes.string}), 819 | {foo: 'bar'}, 820 | 'Property `testProp` of component `testComponent` has invalid PropType notation inside objectOf.', 821 | ); 822 | }); 823 | 824 | it('should support the objectOf propTypes', () => { 825 | typeCheckPass(PropTypes.objectOf(PropTypes.number), {a: 1, b: 2, c: 3}); 826 | typeCheckPass(PropTypes.objectOf(PropTypes.string), { 827 | a: 'a', 828 | b: 'b', 829 | c: 'c', 830 | }); 831 | typeCheckPass(PropTypes.objectOf(PropTypes.oneOf(['a', 'b'])), { 832 | a: 'a', 833 | b: 'b', 834 | }); 835 | typeCheckPass(PropTypes.objectOf(PropTypes.symbol), { 836 | a: Symbol(), 837 | b: Symbol(), 838 | c: Symbol(), 839 | }); 840 | }); 841 | 842 | it('should support objectOf with complex types', () => { 843 | typeCheckPass( 844 | PropTypes.objectOf(PropTypes.shape({a: PropTypes.number.isRequired})), 845 | {a: {a: 1}, b: {a: 2}}, 846 | ); 847 | 848 | function Thing() {} 849 | typeCheckPass(PropTypes.objectOf(PropTypes.instanceOf(Thing)), { 850 | a: new Thing(), 851 | b: new Thing(), 852 | }); 853 | }); 854 | 855 | it('should warn with invalid items in the object', () => { 856 | typeCheckFail( 857 | PropTypes.objectOf(PropTypes.number), 858 | {a: 1, b: 2, c: 'b'}, 859 | 'Invalid prop `testProp.c` of type `string` supplied to `testComponent`, ' + 860 | 'expected `number`.', 861 | ); 862 | 863 | typeCheckFail( 864 | PropTypes.objectOf(PropTypes.number.isRequired), 865 | {a: 1, b: 2, c: undefined}, 866 | 'Warning: Failed prop type: The prop `testProp.c` is marked as required in `testComponent`, ' + 867 | 'but its value is `undefined`.' 868 | ); 869 | }); 870 | 871 | it('should warn with invalid complex types', () => { 872 | function Thing() {} 873 | const name = Thing.name || '<>'; 874 | 875 | typeCheckFail( 876 | PropTypes.objectOf(PropTypes.instanceOf(Thing)), 877 | {a: new Thing(), b: 'xyz'}, 878 | 'Invalid prop `testProp.b` of type `String` supplied to ' + 879 | '`testComponent`, expected instance of `' + 880 | name + 881 | '`.', 882 | ); 883 | 884 | typeCheckFail( 885 | PropTypes.objectOf(PropTypes.instanceOf(Thing).isRequired), 886 | {a: new Thing(), b: undefined}, 887 | 'Warning: Failed prop type: The prop `testProp.b` is marked as required in `testComponent`, ' + 888 | 'but its value is `undefined`.' 889 | ); 890 | }); 891 | 892 | it('should warn when passed something other than an object', () => { 893 | typeCheckFail( 894 | PropTypes.objectOf(PropTypes.number), 895 | [1, 2], 896 | 'Invalid prop `testProp` of type `array` supplied to ' + 897 | '`testComponent`, expected an object.', 898 | ); 899 | typeCheckFail( 900 | PropTypes.objectOf(PropTypes.number), 901 | 123, 902 | 'Invalid prop `testProp` of type `number` supplied to ' + 903 | '`testComponent`, expected an object.', 904 | ); 905 | typeCheckFail( 906 | PropTypes.objectOf(PropTypes.number), 907 | 'string', 908 | 'Invalid prop `testProp` of type `string` supplied to ' + 909 | '`testComponent`, expected an object.', 910 | ); 911 | typeCheckFail( 912 | PropTypes.objectOf(PropTypes.symbol), 913 | Symbol(), 914 | 'Invalid prop `testProp` of type `symbol` supplied to ' + 915 | '`testComponent`, expected an object.', 916 | ); 917 | }); 918 | 919 | it('should not warn when passing an object with no prototype', () => { 920 | typeCheckPass(PropTypes.objectOf(PropTypes.number), Object.create(null)); 921 | }); 922 | 923 | it('should not warn when passing an empty object', () => { 924 | typeCheckPass(PropTypes.objectOf(PropTypes.number), {}); 925 | }); 926 | 927 | it('should not warn when passing an object with a hasOwnProperty property', () => { 928 | typeCheckPass(PropTypes.objectOf(PropTypes.number), { 929 | hasOwnProperty: 3, 930 | }); 931 | }); 932 | 933 | it('should be implicitly optional and not warn without values', () => { 934 | typeCheckPass(PropTypes.objectOf(PropTypes.number), null); 935 | typeCheckPass(PropTypes.objectOf(PropTypes.number), undefined); 936 | }); 937 | 938 | it('should warn for missing required values', () => { 939 | typeCheckFailRequiredValues( 940 | PropTypes.objectOf(PropTypes.number).isRequired, 941 | ); 942 | }); 943 | 944 | it('should warn if called manually in development', () => { 945 | spyOn(console, 'error'); 946 | expectWarningInDevelopment(PropTypes.objectOf({foo: PropTypes.string}), { 947 | foo: 'bar', 948 | }); 949 | expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), { 950 | a: 1, 951 | b: 2, 952 | c: 'b', 953 | }); 954 | expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), [1, 2]); 955 | expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), null); 956 | expectWarningInDevelopment( 957 | PropTypes.objectOf(PropTypes.number), 958 | undefined, 959 | ); 960 | }); 961 | }); 962 | 963 | describe('OneOf Types', () => { 964 | it('should warn but not error for invalid argument', () => { 965 | spyOn(console, 'error'); 966 | 967 | PropTypes.oneOf('red'); 968 | 969 | expect(console.error).toHaveBeenCalled(); 970 | expect(console.error.calls.argsFor(0)[0]).toContain( 971 | 'Invalid argument supplied to oneOf, expected an array.', 972 | ); 973 | 974 | typeCheckPass(PropTypes.oneOf('red', 'blue'), 'red'); 975 | }); 976 | 977 | it('should warn but not error for invalid multiple arguments', () => { 978 | spyOn(console, 'error'); 979 | 980 | PropTypes.oneOf('red', 'blue'); 981 | 982 | expect(console.error).toHaveBeenCalled(); 983 | expect(console.error.calls.argsFor(0)[0]).toContain( 984 | 'Invalid arguments supplied to oneOf, expected an array, got 2 arguments. ' 985 | + 'A common mistake is to write oneOf(x, y, z) instead of oneOf([x, y, z]).', 986 | ); 987 | 988 | typeCheckPass(PropTypes.oneOf('red', 'blue'), 'red'); 989 | }); 990 | 991 | it('should warn for invalid values', () => { 992 | typeCheckFail( 993 | PropTypes.oneOf(['red', 'blue']), 994 | true, 995 | 'Invalid prop `testProp` of value `true` supplied to ' + 996 | '`testComponent`, expected one of ["red","blue"].', 997 | ); 998 | typeCheckFail( 999 | PropTypes.oneOf(['red', 'blue']), 1000 | [], 1001 | 'Invalid prop `testProp` of value `` supplied to `testComponent`, ' + 1002 | 'expected one of ["red","blue"].', 1003 | ); 1004 | typeCheckFail( 1005 | PropTypes.oneOf(['red', 'blue']), 1006 | '', 1007 | 'Invalid prop `testProp` of value `` supplied to `testComponent`, ' + 1008 | 'expected one of ["red","blue"].', 1009 | ); 1010 | typeCheckFail( 1011 | PropTypes.oneOf([0, 'false']), 1012 | false, 1013 | 'Invalid prop `testProp` of value `false` supplied to ' + 1014 | '`testComponent`, expected one of [0,"false"].', 1015 | ); 1016 | typeCheckFail( 1017 | PropTypes.oneOf([Symbol('red'), Symbol('blue')]), 1018 | Symbol('green'), 1019 | 'Invalid prop `testProp` of value `Symbol(green)` supplied to ' + 1020 | '`testComponent`, expected one of ["Symbol(red)","Symbol(blue)"].', 1021 | ); 1022 | typeCheckFail( 1023 | PropTypes.oneOf([0, 'false']).isRequired, 1024 | undefined, 1025 | 'Warning: Failed prop type: The prop `testProp` is marked as required in `testComponent`, ' + 1026 | 'but its value is `undefined`.' 1027 | ); 1028 | typeCheckFail( 1029 | PropTypes.oneOf([0, 'false']).isRequired, 1030 | null, 1031 | 'Warning: Failed prop type: The prop `testProp` is marked as required in `testComponent`, ' + 1032 | 'but its value is `null`.' 1033 | ); 1034 | }); 1035 | 1036 | it('does not fail when the valid types contain null or undefined', () => { 1037 | typeCheckFail( 1038 | PropTypes.oneOf([0, 'false', null, undefined]), 1039 | false, 1040 | 'Warning: Failed prop type: Invalid prop `testProp` of value `false` supplied to ' + 1041 | '`testComponent`, expected one of [0,"false",null,null].', 1042 | // TODO: uncomment and fix implementation 1043 | // '`testComponent`, expected one of [0,"false",null,undefined].', 1044 | ); 1045 | }); 1046 | 1047 | it('should not warn for valid values', () => { 1048 | typeCheckPass(PropTypes.oneOf(['red', 'blue']), 'red'); 1049 | typeCheckPass(PropTypes.oneOf(['red', 'blue']), 'blue'); 1050 | typeCheckPass(PropTypes.oneOf(['red', 'blue', NaN]), NaN); 1051 | }); 1052 | 1053 | it('should be implicitly optional and not warn without values', () => { 1054 | typeCheckPass(PropTypes.oneOf(['red', 'blue']), null); 1055 | typeCheckPass(PropTypes.oneOf(['red', 'blue']), undefined); 1056 | }); 1057 | 1058 | it('should warn for missing required values', () => { 1059 | typeCheckFailRequiredValues(PropTypes.oneOf(['red', 'blue']).isRequired); 1060 | }); 1061 | 1062 | it('should warn if called manually in development', () => { 1063 | spyOn(console, 'error'); 1064 | expectWarningInDevelopment(PropTypes.oneOf(['red', 'blue']), true); 1065 | expectWarningInDevelopment(PropTypes.oneOf(['red', 'blue']), null); 1066 | expectWarningInDevelopment(PropTypes.oneOf(['red', 'blue']), undefined); 1067 | }); 1068 | }); 1069 | 1070 | describe('Union Types', () => { 1071 | it('should warn but not error for invalid argument', () => { 1072 | spyOn(console, 'error'); 1073 | 1074 | PropTypes.oneOfType(PropTypes.string, PropTypes.number); 1075 | 1076 | expect(console.error).toHaveBeenCalled(); 1077 | expect(console.error.calls.argsFor(0)[0]).toContain( 1078 | 'Invalid argument supplied to oneOfType, expected an instance of array.', 1079 | ); 1080 | 1081 | typeCheckPass(PropTypes.oneOf(PropTypes.string, PropTypes.number), []); 1082 | }); 1083 | 1084 | it('should warn but for invalid argument type', () => { 1085 | spyOn(console, 'error'); 1086 | 1087 | const types = [undefined, null, false, new Date, /foo/, {}]; 1088 | const expected = ['undefined', 'null', 'a boolean', 'a date', 'a regexp', 'an object']; 1089 | 1090 | for (let i = 0; i < expected.length; i++) { 1091 | const type = types[i]; 1092 | PropTypes.oneOfType([type]); 1093 | expect(console.error).toHaveBeenCalled(); 1094 | expect(console.error.calls.argsFor(0)[0]).toContain( 1095 | 'Invalid argument supplied to oneOfType. Expected an array of check functions, ' + 1096 | 'but received ' + expected[i] + ' at index 0.' 1097 | ); 1098 | console.error.calls.reset(); 1099 | } 1100 | 1101 | typeCheckPass(PropTypes.oneOf(PropTypes.string, PropTypes.number), []); 1102 | }); 1103 | 1104 | it('should warn if none of the types are valid', () => { 1105 | typeCheckFail( 1106 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 1107 | [], 1108 | 'Invalid prop `testProp` supplied to `testComponent`, expected one of type [string, number].', 1109 | ); 1110 | 1111 | const checker = PropTypes.oneOfType([ 1112 | PropTypes.shape({a: PropTypes.number.isRequired}), 1113 | PropTypes.shape({b: PropTypes.number.isRequired}), 1114 | ]); 1115 | typeCheckFail( 1116 | checker, 1117 | {c: 1}, 1118 | 'Invalid prop `testProp` supplied to `testComponent`.', 1119 | ); 1120 | }); 1121 | 1122 | it('should not warn if one of the types are valid', () => { 1123 | let checker = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); 1124 | typeCheckPass(checker, null); 1125 | typeCheckPass(checker, 'foo'); 1126 | typeCheckPass(checker, 123); 1127 | 1128 | checker = PropTypes.oneOfType([ 1129 | PropTypes.shape({a: PropTypes.number.isRequired}), 1130 | PropTypes.shape({b: PropTypes.number.isRequired}), 1131 | ]); 1132 | typeCheckPass(checker, {a: 1}); 1133 | typeCheckPass(checker, {b: 1}); 1134 | }); 1135 | 1136 | it('should be implicitly optional and not warn without values', () => { 1137 | typeCheckPass( 1138 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 1139 | null, 1140 | ); 1141 | typeCheckPass( 1142 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 1143 | undefined, 1144 | ); 1145 | }); 1146 | 1147 | it('should warn for missing required values', () => { 1148 | typeCheckFailRequiredValues( 1149 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, 1150 | ); 1151 | }); 1152 | 1153 | it('should warn if called manually in development', () => { 1154 | spyOn(console, 'error'); 1155 | expectWarningInDevelopment( 1156 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 1157 | [], 1158 | ); 1159 | expectWarningInDevelopment( 1160 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 1161 | null, 1162 | ); 1163 | expectWarningInDevelopment( 1164 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 1165 | undefined, 1166 | ); 1167 | }); 1168 | }); 1169 | 1170 | describe('Shape Types', () => { 1171 | it('should warn for non objects', () => { 1172 | typeCheckFail( 1173 | PropTypes.shape({}), 1174 | 'some string', 1175 | 'Invalid prop `testProp` of type `string` supplied to ' + 1176 | '`testComponent`, expected `object`.', 1177 | ); 1178 | typeCheckFail( 1179 | PropTypes.shape({}), 1180 | ['array'], 1181 | 'Invalid prop `testProp` of type `array` supplied to ' + 1182 | '`testComponent`, expected `object`.', 1183 | ); 1184 | }); 1185 | 1186 | it('should not warn for empty values', () => { 1187 | typeCheckPass(PropTypes.shape({}), undefined); 1188 | typeCheckPass(PropTypes.shape({}), null); 1189 | typeCheckPass(PropTypes.shape({}), {}); 1190 | }); 1191 | 1192 | it('should not warn for an empty object', () => { 1193 | typeCheckPass(PropTypes.shape({}).isRequired, {}); 1194 | }); 1195 | 1196 | it('should not warn for non specified types', () => { 1197 | typeCheckPass(PropTypes.shape({}), {key: 1}); 1198 | }); 1199 | 1200 | it('should not warn for valid types', () => { 1201 | typeCheckPass(PropTypes.shape({key: PropTypes.number}), {key: 1}); 1202 | }); 1203 | 1204 | it('should warn for required valid types', () => { 1205 | typeCheckFail( 1206 | PropTypes.shape({key: PropTypes.number.isRequired}), 1207 | {}, 1208 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 1209 | 'but its value is `undefined`.', 1210 | ); 1211 | 1212 | typeCheckFail( 1213 | PropTypes.shape({key: PropTypes.number.isRequired}), 1214 | {key: undefined}, 1215 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 1216 | 'but its value is `undefined`.', 1217 | ); 1218 | 1219 | typeCheckFail( 1220 | PropTypes.shape({key: PropTypes.number.isRequired}), 1221 | {key: null}, 1222 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 1223 | 'but its value is `null`.', 1224 | ); 1225 | }); 1226 | 1227 | it('should warn for the first required type', () => { 1228 | typeCheckFail( 1229 | PropTypes.shape({ 1230 | key: PropTypes.number.isRequired, 1231 | secondKey: PropTypes.number.isRequired, 1232 | }), 1233 | {}, 1234 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 1235 | 'but its value is `undefined`.', 1236 | ); 1237 | }); 1238 | 1239 | it('should warn for invalid key types', () => { 1240 | typeCheckFail( 1241 | PropTypes.shape({key: PropTypes.number}), 1242 | {key: 'abc'}, 1243 | 'Invalid prop `testProp.key` of type `string` supplied to `testComponent`, ' + 1244 | 'expected `number`.', 1245 | ); 1246 | }); 1247 | 1248 | it('should be implicitly optional and not warn without values', () => { 1249 | typeCheckPass( 1250 | PropTypes.shape(PropTypes.shape({key: PropTypes.number})), 1251 | null, 1252 | ); 1253 | typeCheckPass( 1254 | PropTypes.shape(PropTypes.shape({key: PropTypes.number})), 1255 | undefined, 1256 | ); 1257 | }); 1258 | 1259 | it('should warn for missing required values', () => { 1260 | typeCheckFailRequiredValues( 1261 | PropTypes.shape({key: PropTypes.number}).isRequired, 1262 | ); 1263 | }); 1264 | 1265 | it('should warn if called manually in development', () => { 1266 | spyOn(console, 'error'); 1267 | expectWarningInDevelopment(PropTypes.shape({}), 'some string'); 1268 | expectWarningInDevelopment(PropTypes.shape({foo: PropTypes.number}), { 1269 | foo: 42, 1270 | }); 1271 | expectWarningInDevelopment( 1272 | PropTypes.shape({key: PropTypes.number}).isRequired, 1273 | null, 1274 | ); 1275 | expectWarningInDevelopment( 1276 | PropTypes.shape({key: PropTypes.number}).isRequired, 1277 | undefined, 1278 | ); 1279 | expectWarningInDevelopment(PropTypes.element,
); 1280 | }); 1281 | }); 1282 | 1283 | describe('Exact Types', () => { 1284 | it('should warn for non objects', () => { 1285 | typeCheckFail( 1286 | PropTypes.exact({}), 1287 | 'some string', 1288 | 'Invalid prop `testProp` of type `string` supplied to ' + 1289 | '`testComponent`, expected `object`.', 1290 | ); 1291 | typeCheckFail( 1292 | PropTypes.exact({}), 1293 | ['array'], 1294 | 'Invalid prop `testProp` of type `array` supplied to ' + 1295 | '`testComponent`, expected `object`.', 1296 | ); 1297 | }); 1298 | 1299 | it('should not warn for empty values', () => { 1300 | typeCheckPass(PropTypes.exact({}), undefined); 1301 | typeCheckPass(PropTypes.exact({}), null); 1302 | typeCheckPass(PropTypes.exact({}), {}); 1303 | }); 1304 | 1305 | it('should not warn for an empty object', () => { 1306 | typeCheckPass(PropTypes.exact({}).isRequired, {}); 1307 | }); 1308 | 1309 | it('should warn for non specified types', () => { 1310 | typeCheckFail( 1311 | PropTypes.exact({}), 1312 | {key: 1}, 1313 | 'Warning: Failed prop type: Invalid prop `testProp` key `key` supplied to `testComponent`.' + 1314 | '\nBad object: {' + 1315 | '\n \"key\": 1' + 1316 | '\n}' + 1317 | '\nValid keys: []' 1318 | ); 1319 | }); 1320 | 1321 | it('should not warn for valid types', () => { 1322 | typeCheckPass(PropTypes.exact({key: PropTypes.number}), {key: 1}); 1323 | }); 1324 | 1325 | it('should warn for required valid types', () => { 1326 | typeCheckFail( 1327 | PropTypes.exact({key: PropTypes.number.isRequired}), 1328 | {}, 1329 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 1330 | 'but its value is `undefined`.', 1331 | ); 1332 | }); 1333 | 1334 | it('should warn for the first required type', () => { 1335 | typeCheckFail( 1336 | PropTypes.exact({ 1337 | key: PropTypes.number.isRequired, 1338 | secondKey: PropTypes.number.isRequired, 1339 | }), 1340 | {}, 1341 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 1342 | 'but its value is `undefined`.', 1343 | ); 1344 | }); 1345 | 1346 | it('should warn for invalid key types', () => { 1347 | typeCheckFail( 1348 | PropTypes.exact({key: PropTypes.number}), 1349 | {key: 'abc'}, 1350 | 'Invalid prop `testProp.key` of type `string` supplied to `testComponent`, ' + 1351 | 'expected `number`.', 1352 | ); 1353 | }); 1354 | 1355 | it('should be implicitly optional and not warn without values', () => { 1356 | typeCheckPass( 1357 | PropTypes.exact(PropTypes.exact({key: PropTypes.number})), 1358 | null, 1359 | ); 1360 | typeCheckPass( 1361 | PropTypes.exact(PropTypes.exact({key: PropTypes.number})), 1362 | undefined, 1363 | ); 1364 | }); 1365 | 1366 | it('should warn for missing required values', () => { 1367 | typeCheckFailRequiredValues( 1368 | PropTypes.exact({key: PropTypes.number}).isRequired, 1369 | ); 1370 | }); 1371 | 1372 | it('should warn if called manually in development', () => { 1373 | spyOn(console, 'error'); 1374 | expectWarningInDevelopment(PropTypes.exact({}), 'some string'); 1375 | expectWarningInDevelopment(PropTypes.exact({foo: PropTypes.number}), { 1376 | foo: 42, 1377 | }); 1378 | expectWarningInDevelopment( 1379 | PropTypes.exact({key: PropTypes.number}).isRequired, 1380 | null, 1381 | ); 1382 | expectWarningInDevelopment( 1383 | PropTypes.exact({key: PropTypes.number}).isRequired, 1384 | undefined, 1385 | ); 1386 | expectWarningInDevelopment(PropTypes.element,
); 1387 | }); 1388 | 1389 | it('works with oneOfType', () => { 1390 | typeCheckPass( 1391 | PropTypes.exact({ foo: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }), 1392 | { foo: 42 } 1393 | ); 1394 | typeCheckPass( 1395 | PropTypes.exact({ foo: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }), 1396 | { foo: '42' } 1397 | ); 1398 | typeCheckFail( 1399 | PropTypes.exact({ foo: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }), 1400 | { foo: 42, bar: 'what is 6 * 7' }, 1401 | `Warning: Failed prop type: Invalid prop \`testProp\` key \`bar\` supplied to \`testComponent\`. 1402 | Bad object: { 1403 | "foo": 42, 1404 | "bar": "what is 6 * 7" 1405 | } 1406 | Valid keys: [ 1407 | "foo" 1408 | ]` 1409 | ); 1410 | }); 1411 | 1412 | it('works with a custom propType', () => { 1413 | typeCheckFail( 1414 | PropTypes.oneOfType([() => new Error('hi')]), 1415 | {}, 1416 | 'Warning: Failed prop type: Invalid prop `testProp` supplied to `testComponent`.' 1417 | ) 1418 | }); 1419 | }); 1420 | 1421 | describe('Symbol Type', () => { 1422 | it('should warn for non-symbol', () => { 1423 | typeCheckFail( 1424 | PropTypes.symbol, 1425 | 'hello', 1426 | 'Invalid prop `testProp` of type `string` supplied to ' + 1427 | '`testComponent`, expected `symbol`.', 1428 | ); 1429 | typeCheckFail( 1430 | PropTypes.symbol, 1431 | function() {}, 1432 | 'Invalid prop `testProp` of type `function` supplied to ' + 1433 | '`testComponent`, expected `symbol`.', 1434 | ); 1435 | typeCheckFail( 1436 | PropTypes.symbol, 1437 | { 1438 | '@@toStringTag': 'Katana', 1439 | }, 1440 | 'Invalid prop `testProp` of type `object` supplied to ' + 1441 | '`testComponent`, expected `symbol`.', 1442 | ); 1443 | }); 1444 | 1445 | it('should not warn for a polyfilled Symbol', () => { 1446 | const CoreSymbol = require('core-js/library/es6/symbol'); 1447 | typeCheckPass(PropTypes.symbol, CoreSymbol('core-js')); 1448 | }); 1449 | }); 1450 | }); 1451 | -------------------------------------------------------------------------------- /__tests__/PropTypesProductionReact15-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @emails react-core 8 | */ 9 | 10 | 'use strict'; 11 | 12 | let React; 13 | let PropTypes; 14 | 15 | function resetWarningCache() { 16 | jest.resetModules(); 17 | 18 | // Set production mode throughout this test. 19 | process.env.NODE_ENV = 'production'; 20 | React = require('react'); 21 | // We are testing that when imported in the same way React 15 imports `prop-types`, 22 | // it just suppresses warnings but doesn't actually throw when calling validators. 23 | PropTypes = require('../factory')(React.isValidElement); 24 | } 25 | 26 | function expectNoop(declaration, value) { 27 | if (!console.error.calls) { 28 | spyOn(console, 'error'); 29 | } else { 30 | console.error.calls.reset(); 31 | } 32 | 33 | const props = {testProp: value}; 34 | const propName = 'testProp' + Math.random().toString(); 35 | const componentName = 'testComponent' + Math.random().toString(); 36 | // Try calling it manually 37 | for (let i = 0; i < 3; i++) { 38 | declaration(props, propName, componentName, 'prop'); 39 | } 40 | // Try calling it via checkPropTypes 41 | const propTypes = { 42 | testProp: declaration, 43 | }; 44 | PropTypes.checkPropTypes(propTypes, props, 'prop', 'testComponent'); 45 | 46 | // They should all be no-ops 47 | expect(console.error.calls.count()).toBe(0); 48 | console.error.calls.reset(); 49 | } 50 | 51 | describe('PropTypesProductionReact15', () => { 52 | beforeEach(() => { 53 | resetWarningCache(); 54 | }); 55 | 56 | describe('Primitive Types', () => { 57 | it('should warn for invalid strings', () => { 58 | expectNoop( 59 | PropTypes.string, 60 | [], 61 | 'Invalid prop `testProp` of type `array` supplied to ' + 62 | '`testComponent`, expected `string`.', 63 | ); 64 | expectNoop( 65 | PropTypes.string, 66 | false, 67 | 'Invalid prop `testProp` of type `boolean` supplied to ' + 68 | '`testComponent`, expected `string`.', 69 | ); 70 | expectNoop( 71 | PropTypes.string, 72 | 0, 73 | 'Invalid prop `testProp` of type `number` supplied to ' + 74 | '`testComponent`, expected `string`.', 75 | ); 76 | expectNoop( 77 | PropTypes.string, 78 | {}, 79 | 'Invalid prop `testProp` of type `object` supplied to ' + 80 | '`testComponent`, expected `string`.', 81 | ); 82 | expectNoop( 83 | PropTypes.string, 84 | Symbol(), 85 | 'Invalid prop `testProp` of type `symbol` supplied to ' + 86 | '`testComponent`, expected `string`.', 87 | ); 88 | }); 89 | 90 | it('should fail date and regexp correctly', () => { 91 | expectNoop( 92 | PropTypes.string, 93 | new Date(), 94 | 'Invalid prop `testProp` of type `date` supplied to ' + 95 | '`testComponent`, expected `string`.', 96 | ); 97 | expectNoop( 98 | PropTypes.string, 99 | /please/, 100 | 'Invalid prop `testProp` of type `regexp` supplied to ' + 101 | '`testComponent`, expected `string`.', 102 | ); 103 | }); 104 | 105 | it('should not warn for valid values', () => { 106 | expectNoop(PropTypes.array, []); 107 | if (typeof BigInt === 'function') { 108 | expectNoop(PropTypes.bigint, BigInt(0)); 109 | } 110 | expectNoop(PropTypes.bool, false); 111 | expectNoop(PropTypes.func, function() {}); 112 | expectNoop(PropTypes.number, 0); 113 | expectNoop(PropTypes.string, ''); 114 | expectNoop(PropTypes.object, {}); 115 | expectNoop(PropTypes.object, new Date()); 116 | expectNoop(PropTypes.object, /please/); 117 | expectNoop(PropTypes.symbol, Symbol()); 118 | }); 119 | 120 | it('should be implicitly optional and not warn without values', () => { 121 | expectNoop(PropTypes.string, null); 122 | expectNoop(PropTypes.string, undefined); 123 | }); 124 | 125 | it('should warn for missing required values', () => { 126 | expectNoop(PropTypes.string.isRequired); 127 | expectNoop(PropTypes.array.isRequired); 128 | expectNoop(PropTypes.symbol.isRequired); 129 | expectNoop(PropTypes.number.isRequired); 130 | expectNoop(PropTypes.bigint.isRequired); 131 | expectNoop(PropTypes.bool.isRequired); 132 | expectNoop(PropTypes.func.isRequired); 133 | expectNoop(PropTypes.shape({}).isRequired); 134 | }); 135 | 136 | it('should warn if called manually in development', () => { 137 | spyOn(console, 'error'); 138 | expectNoop(PropTypes.array, /please/); 139 | expectNoop(PropTypes.array, []); 140 | expectNoop(PropTypes.array.isRequired, /please/); 141 | expectNoop(PropTypes.array.isRequired, []); 142 | expectNoop(PropTypes.array.isRequired, null); 143 | expectNoop(PropTypes.array.isRequired, undefined); 144 | expectNoop(PropTypes.bigint, function() {}); 145 | expectNoop(PropTypes.bigint, 42); 146 | if (typeof BigInt === 'function') { 147 | expectNoop(PropTypes.bigint, BigInt(42)); 148 | } 149 | expectNoop(PropTypes.bigint.isRequired, function() {}); 150 | expectNoop(PropTypes.bigint.isRequired, 42); 151 | expectNoop(PropTypes.bigint.isRequired, null); 152 | expectNoop(PropTypes.bigint.isRequired, undefined); 153 | expectNoop(PropTypes.bool, []); 154 | expectNoop(PropTypes.bool, true); 155 | expectNoop(PropTypes.bool.isRequired, []); 156 | expectNoop(PropTypes.bool.isRequired, true); 157 | expectNoop(PropTypes.bool.isRequired, null); 158 | expectNoop(PropTypes.bool.isRequired, undefined); 159 | expectNoop(PropTypes.func, false); 160 | expectNoop(PropTypes.func, function() {}); 161 | expectNoop(PropTypes.func.isRequired, false); 162 | expectNoop(PropTypes.func.isRequired, function() {}); 163 | expectNoop(PropTypes.func.isRequired, null); 164 | expectNoop(PropTypes.func.isRequired, undefined); 165 | expectNoop(PropTypes.number, function() {}); 166 | expectNoop(PropTypes.number, 42); 167 | expectNoop(PropTypes.number.isRequired, function() {}); 168 | expectNoop(PropTypes.number.isRequired, 42); 169 | expectNoop(PropTypes.number.isRequired, null); 170 | expectNoop(PropTypes.number.isRequired, undefined); 171 | expectNoop(PropTypes.string, 0); 172 | expectNoop(PropTypes.string, 'foo'); 173 | expectNoop(PropTypes.string.isRequired, 0); 174 | expectNoop(PropTypes.string.isRequired, 'foo'); 175 | expectNoop(PropTypes.string.isRequired, null); 176 | expectNoop(PropTypes.string.isRequired, undefined); 177 | expectNoop(PropTypes.symbol, 0); 178 | expectNoop(PropTypes.symbol, Symbol('Foo')); 179 | expectNoop(PropTypes.symbol.isRequired, 0); 180 | expectNoop(PropTypes.symbol.isRequired, Symbol('Foo')); 181 | expectNoop(PropTypes.symbol.isRequired, null); 182 | expectNoop(PropTypes.symbol.isRequired, undefined); 183 | expectNoop(PropTypes.object, ''); 184 | expectNoop(PropTypes.object, {foo: 'bar'}); 185 | expectNoop(PropTypes.object.isRequired, ''); 186 | expectNoop(PropTypes.object.isRequired, {foo: 'bar'}); 187 | expectNoop(PropTypes.object.isRequired, null); 188 | expectNoop(PropTypes.object.isRequired, undefined); 189 | }); 190 | }); 191 | 192 | describe('Any type', () => { 193 | it('should accept any value', () => { 194 | expectNoop(PropTypes.any, 0); 195 | expectNoop(PropTypes.any, 'str'); 196 | expectNoop(PropTypes.any, []); 197 | expectNoop(PropTypes.any, Symbol()); 198 | }); 199 | 200 | it('should be implicitly optional and not warn without values', () => { 201 | expectNoop(PropTypes.any, null); 202 | expectNoop(PropTypes.any, undefined); 203 | }); 204 | 205 | it('should warn for missing required values', () => { 206 | expectNoop(PropTypes.any.isRequired); 207 | }); 208 | 209 | it('should warn if called manually in development', () => { 210 | spyOn(console, 'error'); 211 | expectNoop(PropTypes.any, null); 212 | expectNoop(PropTypes.any.isRequired, null); 213 | expectNoop(PropTypes.any.isRequired, undefined); 214 | }); 215 | }); 216 | 217 | describe('ArrayOf Type', () => { 218 | it('should fail for invalid argument', () => { 219 | expectNoop( 220 | PropTypes.arrayOf({foo: PropTypes.string}), 221 | {foo: 'bar'}, 222 | 'Property `testProp` of component `testComponent` has invalid PropType notation inside arrayOf.', 223 | ); 224 | }); 225 | 226 | it('should support the arrayOf propTypes', () => { 227 | expectNoop(PropTypes.arrayOf(PropTypes.number), [1, 2, 3]); 228 | if (typeof BigInt === 'function') { 229 | expectNoop(PropTypes.arrayOf(PropTypes.bigint), [BigInt(1), BigInt(2), BigInt(3)]); 230 | } 231 | expectNoop(PropTypes.arrayOf(PropTypes.string), ['a', 'b', 'c']); 232 | expectNoop(PropTypes.arrayOf(PropTypes.oneOf(['a', 'b'])), ['a', 'b']); 233 | expectNoop(PropTypes.arrayOf(PropTypes.symbol), [Symbol(), Symbol()]); 234 | }); 235 | 236 | it('should support arrayOf with complex types', () => { 237 | expectNoop( 238 | PropTypes.arrayOf(PropTypes.shape({a: PropTypes.number.isRequired})), 239 | [{a: 1}, {a: 2}], 240 | ); 241 | 242 | function Thing() {} 243 | expectNoop(PropTypes.arrayOf(PropTypes.instanceOf(Thing)), [ 244 | new Thing(), 245 | new Thing(), 246 | ]); 247 | }); 248 | 249 | it('should warn with invalid items in the array', () => { 250 | expectNoop( 251 | PropTypes.arrayOf(PropTypes.number), 252 | [1, 2, 'b'], 253 | 'Invalid prop `testProp[2]` of type `string` supplied to ' + 254 | '`testComponent`, expected `number`.', 255 | ); 256 | }); 257 | 258 | it('should warn with invalid complex types', () => { 259 | function Thing() {} 260 | const name = Thing.name || '<>'; 261 | 262 | expectNoop( 263 | PropTypes.arrayOf(PropTypes.instanceOf(Thing)), 264 | [new Thing(), 'xyz'], 265 | 'Invalid prop `testProp[1]` of type `String` supplied to ' + 266 | '`testComponent`, expected instance of `' + 267 | name + 268 | '`.', 269 | ); 270 | }); 271 | 272 | it('should warn when passed something other than an array', () => { 273 | expectNoop( 274 | PropTypes.arrayOf(PropTypes.number), 275 | {'0': 'maybe-array', length: 1}, 276 | 'Invalid prop `testProp` of type `object` supplied to ' + 277 | '`testComponent`, expected an array.', 278 | ); 279 | expectNoop( 280 | PropTypes.arrayOf(PropTypes.number), 281 | 123, 282 | 'Invalid prop `testProp` of type `number` supplied to ' + 283 | '`testComponent`, expected an array.', 284 | ); 285 | expectNoop( 286 | PropTypes.arrayOf(PropTypes.number), 287 | 'string', 288 | 'Invalid prop `testProp` of type `string` supplied to ' + 289 | '`testComponent`, expected an array.', 290 | ); 291 | }); 292 | 293 | it('should not warn when passing an empty array', () => { 294 | expectNoop(PropTypes.arrayOf(PropTypes.number), []); 295 | }); 296 | 297 | it('should be implicitly optional and not warn without values', () => { 298 | expectNoop(PropTypes.arrayOf(PropTypes.number), null); 299 | expectNoop(PropTypes.arrayOf(PropTypes.number), undefined); 300 | }); 301 | 302 | it('should warn for missing required values', () => { 303 | expectNoop( 304 | PropTypes.arrayOf(PropTypes.number).isRequired, 305 | ); 306 | }); 307 | 308 | it('should warn if called manually in development', () => { 309 | spyOn(console, 'error'); 310 | expectNoop(PropTypes.arrayOf({foo: PropTypes.string}), { 311 | foo: 'bar', 312 | }); 313 | expectNoop(PropTypes.arrayOf(PropTypes.number), [ 314 | 1, 315 | 2, 316 | 'b', 317 | ]); 318 | expectNoop(PropTypes.arrayOf(PropTypes.number), { 319 | '0': 'maybe-array', 320 | length: 1, 321 | }); 322 | expectNoop( 323 | PropTypes.arrayOf(PropTypes.number).isRequired, 324 | null, 325 | ); 326 | expectNoop( 327 | PropTypes.arrayOf(PropTypes.number).isRequired, 328 | undefined, 329 | ); 330 | }); 331 | }); 332 | 333 | describe('Component Type', () => { 334 | it('should support components', () => { 335 | expectNoop(PropTypes.element,
); 336 | }); 337 | 338 | it('should not support multiple components or scalar values', () => { 339 | expectNoop( 340 | PropTypes.element, 341 | [
,
], 342 | 'Invalid prop `testProp` of type `array` supplied to `testComponent`, ' + 343 | 'expected a single ReactElement.', 344 | ); 345 | expectNoop( 346 | PropTypes.element, 347 | 123, 348 | 'Invalid prop `testProp` of type `number` supplied to `testComponent`, ' + 349 | 'expected a single ReactElement.', 350 | ); 351 | if (typeof BigInt === 'function') { 352 | expectNoop( 353 | PropTypes.element, 354 | BigInt(123), 355 | 'Invalid prop `testProp` of type `bigint` supplied to `testComponent`, ' + 356 | 'expected a single ReactElement.', 357 | ); 358 | } 359 | expectNoop( 360 | PropTypes.element, 361 | 'foo', 362 | 'Invalid prop `testProp` of type `string` supplied to `testComponent`, ' + 363 | 'expected a single ReactElement.', 364 | ); 365 | expectNoop( 366 | PropTypes.element, 367 | false, 368 | 'Invalid prop `testProp` of type `boolean` supplied to `testComponent`, ' + 369 | 'expected a single ReactElement.', 370 | ); 371 | }); 372 | 373 | it('should be implicitly optional and not warn without values', () => { 374 | expectNoop(PropTypes.element, null); 375 | expectNoop(PropTypes.element, undefined); 376 | }); 377 | 378 | it('should warn for missing required values', () => { 379 | expectNoop(PropTypes.element.isRequired); 380 | }); 381 | 382 | it('should warn if called manually in development', () => { 383 | spyOn(console, 'error'); 384 | expectNoop(PropTypes.element, [
,
]); 385 | expectNoop(PropTypes.element,
); 386 | expectNoop(PropTypes.element, 123); 387 | expectNoop(PropTypes.element, 'foo'); 388 | expectNoop(PropTypes.element, false); 389 | expectNoop(PropTypes.element.isRequired, null); 390 | expectNoop(PropTypes.element.isRequired, undefined); 391 | }); 392 | }); 393 | 394 | describe('Instance Types', () => { 395 | it('should warn for invalid instances', () => { 396 | function Person() {} 397 | function Cat() {} 398 | const personName = Person.name || '<>'; 399 | const dateName = Date.name || '<>'; 400 | const regExpName = RegExp.name || '<>'; 401 | 402 | expectNoop( 403 | PropTypes.instanceOf(Person), 404 | false, 405 | 'Invalid prop `testProp` of type `Boolean` supplied to ' + 406 | '`testComponent`, expected instance of `' + 407 | personName + 408 | '`.', 409 | ); 410 | expectNoop( 411 | PropTypes.instanceOf(Person), 412 | {}, 413 | 'Invalid prop `testProp` of type `Object` supplied to ' + 414 | '`testComponent`, expected instance of `' + 415 | personName + 416 | '`.', 417 | ); 418 | expectNoop( 419 | PropTypes.instanceOf(Person), 420 | '', 421 | 'Invalid prop `testProp` of type `String` supplied to ' + 422 | '`testComponent`, expected instance of `' + 423 | personName + 424 | '`.', 425 | ); 426 | expectNoop( 427 | PropTypes.instanceOf(Date), 428 | {}, 429 | 'Invalid prop `testProp` of type `Object` supplied to ' + 430 | '`testComponent`, expected instance of `' + 431 | dateName + 432 | '`.', 433 | ); 434 | expectNoop( 435 | PropTypes.instanceOf(RegExp), 436 | {}, 437 | 'Invalid prop `testProp` of type `Object` supplied to ' + 438 | '`testComponent`, expected instance of `' + 439 | regExpName + 440 | '`.', 441 | ); 442 | expectNoop( 443 | PropTypes.instanceOf(Person), 444 | new Cat(), 445 | 'Invalid prop `testProp` of type `Cat` supplied to ' + 446 | '`testComponent`, expected instance of `' + 447 | personName + 448 | '`.', 449 | ); 450 | expectNoop( 451 | PropTypes.instanceOf(Person), 452 | Object.create(null), 453 | 'Invalid prop `testProp` of type `<>` supplied to ' + 454 | '`testComponent`, expected instance of `' + 455 | personName + 456 | '`.', 457 | ); 458 | }); 459 | 460 | it('should not warn for valid values', () => { 461 | function Person() {} 462 | function Engineer() {} 463 | Engineer.prototype = new Person(); 464 | 465 | expectNoop(PropTypes.instanceOf(Person), new Person()); 466 | expectNoop(PropTypes.instanceOf(Person), new Engineer()); 467 | 468 | expectNoop(PropTypes.instanceOf(Date), new Date()); 469 | expectNoop(PropTypes.instanceOf(RegExp), /please/); 470 | }); 471 | 472 | it('should be implicitly optional and not warn without values', () => { 473 | expectNoop(PropTypes.instanceOf(String), null); 474 | expectNoop(PropTypes.instanceOf(String), undefined); 475 | }); 476 | 477 | it('should warn for missing required values', () => { 478 | expectNoop(PropTypes.instanceOf(String).isRequired); 479 | }); 480 | 481 | it('should warn if called manually in development', () => { 482 | spyOn(console, 'error'); 483 | expectNoop(PropTypes.instanceOf(Date), {}); 484 | expectNoop(PropTypes.instanceOf(Date), new Date()); 485 | expectNoop(PropTypes.instanceOf(Date).isRequired, {}); 486 | expectNoop( 487 | PropTypes.instanceOf(Date).isRequired, 488 | new Date(), 489 | ); 490 | }); 491 | }); 492 | 493 | describe('React Component Types', () => { 494 | it('should warn for invalid values', () => { 495 | const failMessage = 'Invalid prop `testProp` supplied to ' + 496 | '`testComponent`, expected a ReactNode.'; 497 | expectNoop(PropTypes.node, true, failMessage); 498 | expectNoop(PropTypes.node, function() {}, failMessage); 499 | expectNoop(PropTypes.node, {key: function() {}}, failMessage); 500 | expectNoop(PropTypes.node, {key:
}, failMessage); 501 | }); 502 | 503 | it('should not warn for valid values', () => { 504 | function MyComponent() {} 505 | MyComponent.prototype.render = function() { 506 | return
; 507 | }; 508 | expectNoop(PropTypes.node,
); 509 | expectNoop(PropTypes.node, false); 510 | expectNoop(PropTypes.node, ); 511 | expectNoop(PropTypes.node, 'Some string'); 512 | expectNoop(PropTypes.node, []); 513 | expectNoop(PropTypes.node, [ 514 | 123, 515 | 'Some string', 516 |
, 517 | ['Another string', [456], , ], 518 | , 519 | null, 520 | undefined, 521 | ]); 522 | }); 523 | 524 | it('should not warn for iterables', () => { 525 | function MyComponent() {} 526 | MyComponent.prototype.render = function() { 527 | return
; 528 | }; 529 | const iterable = { 530 | '@@iterator': function() { 531 | const i = 0; 532 | return { 533 | next: function() { 534 | const done = ++i > 2; 535 | return {value: done ? undefined : , done: done}; 536 | }, 537 | }; 538 | }, 539 | }; 540 | 541 | expectNoop(PropTypes.node, iterable); 542 | }); 543 | 544 | it('should not warn for entry iterables', () => { 545 | function MyComponent() {} 546 | MyComponent.prototype.render = function() { 547 | return
; 548 | }; 549 | const iterable = { 550 | '@@iterator': function() { 551 | const i = 0; 552 | return { 553 | next: function() { 554 | const done = ++i > 2; 555 | return { 556 | value: done ? undefined : ['#' + i, ], 557 | done: done, 558 | }; 559 | }, 560 | }; 561 | }, 562 | }; 563 | iterable.entries = iterable['@@iterator']; 564 | 565 | expectNoop(PropTypes.node, iterable); 566 | }); 567 | 568 | it('should not warn for null/undefined if not required', () => { 569 | expectNoop(PropTypes.node, null); 570 | expectNoop(PropTypes.node, undefined); 571 | }); 572 | 573 | it('should warn for missing required values', () => { 574 | expectNoop(PropTypes.node.isRequired); 575 | }); 576 | 577 | it('should accept empty array for required props', () => { 578 | expectNoop(PropTypes.node.isRequired, []); 579 | }); 580 | 581 | it('should warn if called manually in development', () => { 582 | spyOn(console, 'error'); 583 | expectNoop(PropTypes.node, 'node'); 584 | expectNoop(PropTypes.node, {}); 585 | expectNoop(PropTypes.node.isRequired, 'node'); 586 | expectNoop(PropTypes.node.isRequired, undefined); 587 | expectNoop(PropTypes.node.isRequired, undefined); 588 | }); 589 | }); 590 | 591 | describe('ObjectOf Type', () => { 592 | it('should fail for invalid argument', () => { 593 | expectNoop( 594 | PropTypes.objectOf({foo: PropTypes.string}), 595 | {foo: 'bar'}, 596 | 'Property `testProp` of component `testComponent` has invalid PropType notation inside objectOf.', 597 | ); 598 | }); 599 | 600 | it('should support the objectOf propTypes', () => { 601 | expectNoop(PropTypes.objectOf(PropTypes.number), {a: 1, b: 2, c: 3}); 602 | expectNoop(PropTypes.objectOf(PropTypes.string), { 603 | a: 'a', 604 | b: 'b', 605 | c: 'c', 606 | }); 607 | expectNoop(PropTypes.objectOf(PropTypes.oneOf(['a', 'b'])), { 608 | a: 'a', 609 | b: 'b', 610 | }); 611 | expectNoop(PropTypes.objectOf(PropTypes.symbol), { 612 | a: Symbol(), 613 | b: Symbol(), 614 | c: Symbol(), 615 | }); 616 | }); 617 | 618 | it('should support objectOf with complex types', () => { 619 | expectNoop( 620 | PropTypes.objectOf(PropTypes.shape({a: PropTypes.number.isRequired})), 621 | {a: {a: 1}, b: {a: 2}}, 622 | ); 623 | 624 | function Thing() {} 625 | expectNoop(PropTypes.objectOf(PropTypes.instanceOf(Thing)), { 626 | a: new Thing(), 627 | b: new Thing(), 628 | }); 629 | }); 630 | 631 | it('should warn with invalid items in the object', () => { 632 | expectNoop( 633 | PropTypes.objectOf(PropTypes.number), 634 | {a: 1, b: 2, c: 'b'}, 635 | 'Invalid prop `testProp.c` of type `string` supplied to `testComponent`, ' + 636 | 'expected `number`.', 637 | ); 638 | 639 | expectNoop( 640 | PropTypes.objectOf(PropTypes.number.isRequired), 641 | {a: 1, b: 2, c: undefined}, 642 | 'Warning: Failed prop type: The prop `testProp.c` is marked as required in `testComponent`, ' + 643 | 'but its value is `undefined`.' 644 | ); 645 | }); 646 | 647 | it('should warn with invalid complex types', () => { 648 | function Thing() {} 649 | const name = Thing.name || '<>'; 650 | 651 | expectNoop( 652 | PropTypes.objectOf(PropTypes.instanceOf(Thing)), 653 | {a: new Thing(), b: 'xyz'}, 654 | 'Invalid prop `testProp.b` of type `String` supplied to ' + 655 | '`testComponent`, expected instance of `' + 656 | name + 657 | '`.', 658 | ); 659 | 660 | expectNoop( 661 | PropTypes.objectOf(PropTypes.instanceOf(Thing).isRequired), 662 | {a: new Thing(), b: undefined}, 663 | 'Warning: Failed prop type: The prop `testProp.b` is marked as required in `testComponent`, ' + 664 | 'but its value is `undefined`.' 665 | ); 666 | }); 667 | 668 | it('should warn when passed something other than an object', () => { 669 | expectNoop( 670 | PropTypes.objectOf(PropTypes.number), 671 | [1, 2], 672 | 'Invalid prop `testProp` of type `array` supplied to ' + 673 | '`testComponent`, expected an object.', 674 | ); 675 | expectNoop( 676 | PropTypes.objectOf(PropTypes.number), 677 | 123, 678 | 'Invalid prop `testProp` of type `number` supplied to ' + 679 | '`testComponent`, expected an object.', 680 | ); 681 | expectNoop( 682 | PropTypes.objectOf(PropTypes.number), 683 | 'string', 684 | 'Invalid prop `testProp` of type `string` supplied to ' + 685 | '`testComponent`, expected an object.', 686 | ); 687 | expectNoop( 688 | PropTypes.objectOf(PropTypes.symbol), 689 | Symbol(), 690 | 'Invalid prop `testProp` of type `symbol` supplied to ' + 691 | '`testComponent`, expected an object.', 692 | ); 693 | }); 694 | 695 | it('should not warn when passing an object with no prototype', () => { 696 | expectNoop(PropTypes.objectOf(PropTypes.number), Object.create(null)); 697 | }); 698 | 699 | it('should not warn when passing an empty object', () => { 700 | expectNoop(PropTypes.objectOf(PropTypes.number), {}); 701 | }); 702 | 703 | it('should not warn when passing an object with a hasOwnProperty property', () => { 704 | expectNoop(PropTypes.objectOf(PropTypes.number), { 705 | hasOwnProperty: 3, 706 | }); 707 | }); 708 | 709 | it('should be implicitly optional and not warn without values', () => { 710 | expectNoop(PropTypes.objectOf(PropTypes.number), null); 711 | expectNoop(PropTypes.objectOf(PropTypes.number), undefined); 712 | }); 713 | 714 | it('should warn for missing required values', () => { 715 | expectNoop( 716 | PropTypes.objectOf(PropTypes.number).isRequired, 717 | ); 718 | }); 719 | 720 | it('should warn if called manually in development', () => { 721 | spyOn(console, 'error'); 722 | expectNoop(PropTypes.objectOf({foo: PropTypes.string}), { 723 | foo: 'bar', 724 | }); 725 | expectNoop(PropTypes.objectOf(PropTypes.number), { 726 | a: 1, 727 | b: 2, 728 | c: 'b', 729 | }); 730 | expectNoop(PropTypes.objectOf(PropTypes.number), [1, 2]); 731 | expectNoop(PropTypes.objectOf(PropTypes.number), null); 732 | expectNoop( 733 | PropTypes.objectOf(PropTypes.number), 734 | undefined, 735 | ); 736 | }); 737 | }); 738 | 739 | describe('OneOf Types', () => { 740 | it('should ignore invalid argument', () => { 741 | spyOn(console, 'error'); 742 | 743 | PropTypes.oneOf('red', 'blue'); 744 | 745 | expect(console.error).not.toHaveBeenCalled(); 746 | expectNoop(PropTypes.oneOf('red', 'blue'), 'red'); 747 | }); 748 | 749 | it('should ignore invalid values', () => { 750 | expectNoop( 751 | PropTypes.oneOf(['red', 'blue']), 752 | true, 753 | 'Invalid prop `testProp` of value `true` supplied to ' + 754 | '`testComponent`, expected one of ["red","blue"].', 755 | ); 756 | expectNoop( 757 | PropTypes.oneOf(['red', 'blue']), 758 | [], 759 | 'Invalid prop `testProp` of value `` supplied to `testComponent`, ' + 760 | 'expected one of ["red","blue"].', 761 | ); 762 | expectNoop( 763 | PropTypes.oneOf(['red', 'blue']), 764 | '', 765 | 'Invalid prop `testProp` of value `` supplied to `testComponent`, ' + 766 | 'expected one of ["red","blue"].', 767 | ); 768 | expectNoop( 769 | PropTypes.oneOf([0, 'false']), 770 | false, 771 | 'Invalid prop `testProp` of value `false` supplied to ' + 772 | '`testComponent`, expected one of [0,"false"].', 773 | ); 774 | expectNoop( 775 | PropTypes.oneOf([Symbol('red'), Symbol('blue')]), 776 | Symbol('green'), 777 | 'Invalid prop `testProp` of value `Symbol(green)` supplied to ' + 778 | '`testComponent`, expected one of ["Symbol(red)","Symbol(blue)"].', 779 | ); 780 | expectNoop( 781 | PropTypes.oneOf([0, 'false']).isRequired, 782 | undefined, 783 | 'Warning: Failed prop type: The prop `testProp` is marked as required in `testComponent`, ' + 784 | 'but its value is `undefined`.' 785 | ); 786 | expectNoop( 787 | PropTypes.oneOf([0, 'false']).isRequired, 788 | null, 789 | 'Warning: Failed prop type: The prop `testProp` is marked as required in `testComponent`, ' + 790 | 'but its value is `null`.' 791 | ); 792 | }); 793 | 794 | it('does not fail when the valid types contain null or undefined', () => { 795 | expectNoop( 796 | PropTypes.oneOf([0, 'false', null, undefined]), 797 | false, 798 | 'Warning: Failed prop type: Invalid prop `testProp` of value `false` supplied to ' + 799 | '`testComponent`, expected one of [0,"false",null,undefined].', 800 | ); 801 | }); 802 | 803 | it('should not warn for valid values', () => { 804 | expectNoop(PropTypes.oneOf(['red', 'blue']), 'red'); 805 | expectNoop(PropTypes.oneOf(['red', 'blue']), 'blue'); 806 | expectNoop(PropTypes.oneOf(['red', 'blue', NaN]), NaN); 807 | }); 808 | 809 | it('should be implicitly optional and not warn without values', () => { 810 | expectNoop(PropTypes.oneOf(['red', 'blue']), null); 811 | expectNoop(PropTypes.oneOf(['red', 'blue']), undefined); 812 | }); 813 | 814 | it('should warn for missing required values', () => { 815 | expectNoop(PropTypes.oneOf(['red', 'blue']).isRequired); 816 | }); 817 | 818 | it('should warn if called manually in development', () => { 819 | spyOn(console, 'error'); 820 | expectNoop(PropTypes.oneOf(['red', 'blue']), true); 821 | expectNoop(PropTypes.oneOf(['red', 'blue']), null); 822 | expectNoop(PropTypes.oneOf(['red', 'blue']), undefined); 823 | }); 824 | }); 825 | 826 | describe('Union Types', () => { 827 | it('should ignore invalid argument', () => { 828 | spyOn(console, 'error'); 829 | 830 | PropTypes.oneOfType(PropTypes.string, PropTypes.number); 831 | 832 | expect(console.error).not.toHaveBeenCalled(); 833 | expectNoop(PropTypes.oneOf(PropTypes.string, PropTypes.number), []); 834 | }); 835 | 836 | it('should warn if none of the types are valid', () => { 837 | expectNoop( 838 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 839 | [], 840 | 'Invalid prop `testProp` supplied to `testComponent`.', 841 | ); 842 | 843 | const checker = PropTypes.oneOfType([ 844 | PropTypes.shape({a: PropTypes.number.isRequired}), 845 | PropTypes.shape({b: PropTypes.number.isRequired}), 846 | ]); 847 | expectNoop( 848 | checker, 849 | {c: 1}, 850 | 'Invalid prop `testProp` supplied to `testComponent`.', 851 | ); 852 | }); 853 | 854 | it('should not warn if one of the types are valid', () => { 855 | let checker = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); 856 | expectNoop(checker, null); 857 | expectNoop(checker, 'foo'); 858 | expectNoop(checker, 123); 859 | 860 | checker = PropTypes.oneOfType([ 861 | PropTypes.shape({a: PropTypes.number.isRequired}), 862 | PropTypes.shape({b: PropTypes.number.isRequired}), 863 | ]); 864 | expectNoop(checker, {a: 1}); 865 | expectNoop(checker, {b: 1}); 866 | }); 867 | 868 | it('should be implicitly optional and not warn without values', () => { 869 | expectNoop( 870 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 871 | null, 872 | ); 873 | expectNoop( 874 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 875 | undefined, 876 | ); 877 | }); 878 | 879 | it('should warn for missing required values', () => { 880 | expectNoop( 881 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, 882 | ); 883 | }); 884 | 885 | it('should warn if called manually in development', () => { 886 | spyOn(console, 'error'); 887 | expectNoop( 888 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 889 | [], 890 | ); 891 | expectNoop( 892 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 893 | null, 894 | ); 895 | expectNoop( 896 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 897 | undefined, 898 | ); 899 | }); 900 | 901 | it('should not warn if setup without checker function', () => { 902 | expectNoop( 903 | PropTypes.oneOfType([null]), 904 | null, 905 | ); 906 | expectNoop( 907 | PropTypes.oneOfType([undefined]), 908 | undefined, 909 | ); 910 | }); 911 | }); 912 | 913 | describe('Shape Types', () => { 914 | it('should warn for non objects', () => { 915 | expectNoop( 916 | PropTypes.shape({}), 917 | 'some string', 918 | 'Invalid prop `testProp` of type `string` supplied to ' + 919 | '`testComponent`, expected `object`.', 920 | ); 921 | expectNoop( 922 | PropTypes.shape({}), 923 | ['array'], 924 | 'Invalid prop `testProp` of type `array` supplied to ' + 925 | '`testComponent`, expected `object`.', 926 | ); 927 | }); 928 | 929 | it('should not warn for empty values', () => { 930 | expectNoop(PropTypes.shape({}), undefined); 931 | expectNoop(PropTypes.shape({}), null); 932 | expectNoop(PropTypes.shape({}), {}); 933 | }); 934 | 935 | it('should not warn for an empty object', () => { 936 | expectNoop(PropTypes.shape({}).isRequired, {}); 937 | }); 938 | 939 | it('should not warn for non specified types', () => { 940 | expectNoop(PropTypes.shape({}), {key: 1}); 941 | }); 942 | 943 | it('should not warn for valid types', () => { 944 | expectNoop(PropTypes.shape({key: PropTypes.number}), {key: 1}); 945 | }); 946 | 947 | it('should warn for required valid types', () => { 948 | expectNoop( 949 | PropTypes.shape({key: PropTypes.number.isRequired}), 950 | {}, 951 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 952 | 'but its value is `undefined`.', 953 | ); 954 | 955 | expectNoop( 956 | PropTypes.shape({key: PropTypes.number.isRequired}), 957 | {key: undefined}, 958 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 959 | 'but its value is `undefined`.', 960 | ); 961 | 962 | expectNoop( 963 | PropTypes.shape({key: PropTypes.number.isRequired}), 964 | {key: null}, 965 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 966 | 'but its value is `null`.', 967 | ); 968 | }); 969 | 970 | it('should warn for the first required type', () => { 971 | expectNoop( 972 | PropTypes.shape({ 973 | key: PropTypes.number.isRequired, 974 | secondKey: PropTypes.number.isRequired, 975 | }), 976 | {}, 977 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 978 | 'but its value is `undefined`.', 979 | ); 980 | }); 981 | 982 | it('should warn for invalid key types', () => { 983 | expectNoop( 984 | PropTypes.shape({key: PropTypes.number}), 985 | {key: 'abc'}, 986 | 'Invalid prop `testProp.key` of type `string` supplied to `testComponent`, ' + 987 | 'expected `number`.', 988 | ); 989 | }); 990 | 991 | it('should be implicitly optional and not warn without values', () => { 992 | expectNoop( 993 | PropTypes.shape(PropTypes.shape({key: PropTypes.number})), 994 | null, 995 | ); 996 | expectNoop( 997 | PropTypes.shape(PropTypes.shape({key: PropTypes.number})), 998 | undefined, 999 | ); 1000 | }); 1001 | 1002 | it('should warn for missing required values', () => { 1003 | expectNoop( 1004 | PropTypes.shape({key: PropTypes.number}).isRequired, 1005 | ); 1006 | }); 1007 | 1008 | it('should warn if called manually in development', () => { 1009 | spyOn(console, 'error'); 1010 | expectNoop(PropTypes.shape({}), 'some string'); 1011 | expectNoop(PropTypes.shape({foo: PropTypes.number}), { 1012 | foo: 42, 1013 | }); 1014 | expectNoop( 1015 | PropTypes.shape({key: PropTypes.number}).isRequired, 1016 | null, 1017 | ); 1018 | expectNoop( 1019 | PropTypes.shape({key: PropTypes.number}).isRequired, 1020 | undefined, 1021 | ); 1022 | expectNoop(PropTypes.element,
); 1023 | }); 1024 | }); 1025 | 1026 | describe('Exact Types', () => { 1027 | it('should warn for non objects', () => { 1028 | expectNoop( 1029 | PropTypes.exact({}), 1030 | 'some string' 1031 | ); 1032 | expectNoop( 1033 | PropTypes.exact({}), 1034 | ['array'] 1035 | ); 1036 | }); 1037 | 1038 | it('should not warn for empty values', () => { 1039 | expectNoop(PropTypes.exact({}), undefined); 1040 | expectNoop(PropTypes.exact({}), null); 1041 | expectNoop(PropTypes.exact({}), {}); 1042 | }); 1043 | 1044 | it('should not warn for an empty object', () => { 1045 | expectNoop(PropTypes.exact({}).isRequired, {}); 1046 | }); 1047 | 1048 | it('expectNoop warn for non specified types', () => { 1049 | expectNoop( 1050 | PropTypes.exact({}), 1051 | {key: 1} 1052 | ); 1053 | }); 1054 | 1055 | it('should not warn for valid types', () => { 1056 | expectNoop(PropTypes.exact({key: PropTypes.number}), {key: 1}); 1057 | }); 1058 | 1059 | it('should warn for required valid types', () => { 1060 | expectNoop( 1061 | PropTypes.exact({key: PropTypes.number.isRequired}), 1062 | {} 1063 | ); 1064 | }); 1065 | 1066 | it('should warn for the first required type', () => { 1067 | expectNoop( 1068 | PropTypes.exact({ 1069 | key: PropTypes.number.isRequired, 1070 | secondKey: PropTypes.number.isRequired, 1071 | }), 1072 | {} 1073 | ); 1074 | }); 1075 | 1076 | it('should warn for invalid key types', () => { 1077 | expectNoop( 1078 | PropTypes.exact({key: PropTypes.number}), 1079 | {key: 'abc'} 1080 | ); 1081 | }); 1082 | 1083 | it('should be implicitly optional and not warn without values', () => { 1084 | expectNoop( 1085 | PropTypes.exact(PropTypes.exact({key: PropTypes.number})), 1086 | null, 1087 | ); 1088 | expectNoop( 1089 | PropTypes.exact(PropTypes.exact({key: PropTypes.number})), 1090 | undefined, 1091 | ); 1092 | }); 1093 | 1094 | it('should warn for missing required values', () => { 1095 | expectNoop( 1096 | PropTypes.exact({key: PropTypes.number}).isRequired, 1097 | ); 1098 | }); 1099 | 1100 | it('should warn if called manually in development', () => { 1101 | expectNoop(PropTypes.exact({}), 'some string'); 1102 | expectNoop(PropTypes.exact({foo: PropTypes.number}), { 1103 | foo: 42, 1104 | }); 1105 | expectNoop( 1106 | PropTypes.exact({key: PropTypes.number}).isRequired, 1107 | null, 1108 | ); 1109 | expectNoop( 1110 | PropTypes.exact({key: PropTypes.number}).isRequired, 1111 | undefined, 1112 | ); 1113 | expectNoop(PropTypes.element,
); 1114 | }); 1115 | 1116 | it('works with oneOfType', () => { 1117 | expectNoop( 1118 | PropTypes.exact({ foo: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }), 1119 | { foo: 42 } 1120 | ); 1121 | expectNoop( 1122 | PropTypes.exact({ foo: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }), 1123 | { foo: '42' } 1124 | ); 1125 | expectNoop( 1126 | PropTypes.exact({ foo: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }), 1127 | { foo: 42, bar: 'what is 6 * 7' } 1128 | ); 1129 | }); 1130 | 1131 | it('works with a custom propType', () => { 1132 | expectNoop( 1133 | PropTypes.oneOfType([() => new Error('hi')]), 1134 | {} 1135 | ) 1136 | }); 1137 | }); 1138 | 1139 | describe('Symbol Type', () => { 1140 | it('should warn for non-symbol', () => { 1141 | expectNoop( 1142 | PropTypes.symbol, 1143 | 'hello', 1144 | 'Invalid prop `testProp` of type `string` supplied to ' + 1145 | '`testComponent`, expected `symbol`.', 1146 | ); 1147 | expectNoop( 1148 | PropTypes.symbol, 1149 | function() {}, 1150 | 'Invalid prop `testProp` of type `function` supplied to ' + 1151 | '`testComponent`, expected `symbol`.', 1152 | ); 1153 | expectNoop( 1154 | PropTypes.symbol, 1155 | { 1156 | '@@toStringTag': 'Katana', 1157 | }, 1158 | 'Invalid prop `testProp` of type `object` supplied to ' + 1159 | '`testComponent`, expected `symbol`.', 1160 | ); 1161 | }); 1162 | 1163 | it('should not warn for a polyfilled Symbol', () => { 1164 | const CoreSymbol = require('core-js/library/es6/symbol'); 1165 | expectNoop(PropTypes.symbol, CoreSymbol('core-js')); 1166 | }); 1167 | }); 1168 | 1169 | describe('checkPropTypes', function() { 1170 | describe('checkPropTypes.resetWarningCache', () => { 1171 | it('should provide empty function', () => { 1172 | spyOn(console, 'error'); 1173 | 1174 | var spy = jest.fn(); 1175 | PropTypes.checkPropTypes.resetWarningCache(); 1176 | expect(spy).not.toBeCalled(); 1177 | }); 1178 | }); 1179 | }); 1180 | 1181 | describe('resetWarningCache', () => { 1182 | it('should provide empty function', () => { 1183 | spyOn(console, 'error'); 1184 | 1185 | var spy = jest.fn(); 1186 | PropTypes.resetWarningCache(); 1187 | expect(spy).not.toBeCalled(); 1188 | }); 1189 | }); 1190 | }); 1191 | -------------------------------------------------------------------------------- /__tests__/PropTypesProductionStandalone-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @emails react-core 8 | */ 9 | 10 | 'use strict'; 11 | 12 | describe('PropTypesProductionStandalone', () => { 13 | let React; 14 | let PropTypes; 15 | 16 | function resetWarningCache() { 17 | jest.resetModules(); 18 | process.env.NODE_ENV = 'production'; 19 | 20 | React = require('react'); 21 | PropTypes = require('../index'); 22 | } 23 | 24 | beforeEach(() => { 25 | resetWarningCache(); 26 | }); 27 | 28 | afterEach(() => { 29 | delete process.env.NODE_ENV; 30 | }); 31 | 32 | function getPropTypeWarningMessage(propTypes, object, componentName) { 33 | if (!console.error.calls) { 34 | spyOn(console, 'error'); 35 | } else { 36 | console.error.calls.reset(); 37 | } 38 | resetWarningCache(); 39 | PropTypes.checkPropTypes(propTypes, object, 'prop', 'testComponent'); 40 | const callCount = console.error.calls.count(); 41 | if (callCount > 1) { 42 | throw new Error('Too many warnings.'); 43 | } 44 | const message = console.error.calls.argsFor(0)[0] || null; 45 | console.error.calls.reset(); 46 | 47 | return message; 48 | } 49 | 50 | function expectThrowsInProduction(declaration, value) { 51 | resetWarningCache(); 52 | const props = {testProp: value}; 53 | expect(() => { 54 | declaration(props, 'testProp', 'testComponent', 'prop'); 55 | }).toThrowError( 56 | 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' + 57 | 'Use PropTypes.checkPropTypes() to call them. ' + 58 | 'Read more at http://fb.me/use-check-prop-types' 59 | ); 60 | } 61 | 62 | function typeCheckPass(declaration, value) { 63 | const propTypes = { 64 | testProp: declaration, 65 | }; 66 | const props = { 67 | testProp: value, 68 | }; 69 | const message = getPropTypeWarningMessage(propTypes, props, 'testComponent'); 70 | expect(message).toBe(null); 71 | } 72 | 73 | describe('Primitive Types', () => { 74 | it('should be a no-op', () => { 75 | expectThrowsInProduction(PropTypes.array, /please/); 76 | expectThrowsInProduction(PropTypes.array.isRequired, /please/); 77 | expectThrowsInProduction(PropTypes.array.isRequired, null); 78 | expectThrowsInProduction(PropTypes.array.isRequired, undefined); 79 | expectThrowsInProduction(PropTypes.bigint, function() {}); 80 | expectThrowsInProduction(PropTypes.bigint, 42); 81 | if (typeof BigInt === 'function') { 82 | expectThrowsInProduction(PropTypes.bigint, BigInt(42)); 83 | } 84 | expectThrowsInProduction(PropTypes.bigint.isRequired, function() {}); 85 | expectThrowsInProduction(PropTypes.bigint.isRequired, 42); 86 | expectThrowsInProduction(PropTypes.bigint.isRequired, null); 87 | expectThrowsInProduction(PropTypes.bigint.isRequired, undefined); 88 | expectThrowsInProduction(PropTypes.bool, []); 89 | expectThrowsInProduction(PropTypes.bool.isRequired, []); 90 | expectThrowsInProduction(PropTypes.bool.isRequired, null); 91 | expectThrowsInProduction(PropTypes.bool.isRequired, undefined); 92 | expectThrowsInProduction(PropTypes.func, false); 93 | expectThrowsInProduction(PropTypes.func.isRequired, false); 94 | expectThrowsInProduction(PropTypes.func.isRequired, null); 95 | expectThrowsInProduction(PropTypes.func.isRequired, undefined); 96 | expectThrowsInProduction(PropTypes.number, function() {}); 97 | expectThrowsInProduction(PropTypes.number.isRequired, function() {}); 98 | expectThrowsInProduction(PropTypes.number.isRequired, null); 99 | expectThrowsInProduction(PropTypes.number.isRequired, undefined); 100 | expectThrowsInProduction(PropTypes.string, 0); 101 | expectThrowsInProduction(PropTypes.string.isRequired, 0); 102 | expectThrowsInProduction(PropTypes.string.isRequired, null); 103 | expectThrowsInProduction(PropTypes.string.isRequired, undefined); 104 | expectThrowsInProduction(PropTypes.symbol, 0); 105 | expectThrowsInProduction(PropTypes.symbol.isRequired, 0); 106 | expectThrowsInProduction(PropTypes.symbol.isRequired, null); 107 | expectThrowsInProduction(PropTypes.symbol.isRequired, undefined); 108 | expectThrowsInProduction(PropTypes.object, ''); 109 | expectThrowsInProduction(PropTypes.object.isRequired, ''); 110 | expectThrowsInProduction(PropTypes.object.isRequired, null); 111 | expectThrowsInProduction(PropTypes.object.isRequired, undefined); 112 | }); 113 | }); 114 | 115 | describe('Any Type', () => { 116 | it('should be a no-op', () => { 117 | expectThrowsInProduction(PropTypes.any, null); 118 | expectThrowsInProduction(PropTypes.any.isRequired, null); 119 | expectThrowsInProduction(PropTypes.any.isRequired, undefined); 120 | }); 121 | }); 122 | 123 | describe('ArrayOf Type', () => { 124 | it('should be a no-op', () => { 125 | expectThrowsInProduction(PropTypes.arrayOf({foo: PropTypes.string}), { 126 | foo: 'bar', 127 | }); 128 | expectThrowsInProduction(PropTypes.arrayOf(PropTypes.number), [ 129 | 1, 130 | 2, 131 | 'b', 132 | ]); 133 | expectThrowsInProduction(PropTypes.arrayOf(PropTypes.number), { 134 | '0': 'maybe-array', 135 | length: 1, 136 | }); 137 | expectThrowsInProduction( 138 | PropTypes.arrayOf(PropTypes.number).isRequired, 139 | null, 140 | ); 141 | expectThrowsInProduction( 142 | PropTypes.arrayOf(PropTypes.number).isRequired, 143 | undefined, 144 | ); 145 | }); 146 | }); 147 | 148 | describe('Component Type', () => { 149 | it('should be a no-op', () => { 150 | expectThrowsInProduction(PropTypes.element, [
,
]); 151 | expectThrowsInProduction(PropTypes.element, 123); 152 | if (typeof BigInt === 'function') { 153 | expectThrowsInProduction(PropTypes.element, BigInt(123)); 154 | } 155 | expectThrowsInProduction(PropTypes.element, 'foo'); 156 | expectThrowsInProduction(PropTypes.element, false); 157 | expectThrowsInProduction(PropTypes.element.isRequired, null); 158 | expectThrowsInProduction(PropTypes.element.isRequired, undefined); 159 | }); 160 | }); 161 | 162 | describe('Instance Types', () => { 163 | it('should be a no-op', () => { 164 | expectThrowsInProduction(PropTypes.instanceOf(Date), {}); 165 | expectThrowsInProduction(PropTypes.instanceOf(Date).isRequired, {}); 166 | }); 167 | }); 168 | 169 | describe('React Component Types', () => { 170 | it('should be a no-op', () => { 171 | expectThrowsInProduction(PropTypes.node, {}); 172 | expectThrowsInProduction(PropTypes.node.isRequired, null); 173 | expectThrowsInProduction(PropTypes.node.isRequired, undefined); 174 | }); 175 | }); 176 | 177 | describe('React ElementType Type', () => { 178 | it('should be a no-op', () => { 179 | expectThrowsInProduction(PropTypes.elementType.isRequired, false); 180 | expectThrowsInProduction(PropTypes.elementType.isRequired, {}); 181 | }); 182 | }); 183 | 184 | describe('ObjectOf Type', () => { 185 | it('should be a no-op', () => { 186 | expectThrowsInProduction(PropTypes.objectOf({foo: PropTypes.string}), { 187 | foo: 'bar', 188 | }); 189 | expectThrowsInProduction(PropTypes.objectOf(PropTypes.number), { 190 | a: 1, 191 | b: 2, 192 | c: 'b', 193 | }); 194 | expectThrowsInProduction(PropTypes.objectOf(PropTypes.number), [1, 2]); 195 | expectThrowsInProduction(PropTypes.objectOf(PropTypes.number), null); 196 | expectThrowsInProduction(PropTypes.objectOf(PropTypes.number), undefined); 197 | }); 198 | }); 199 | 200 | describe('OneOf Types', () => { 201 | it('should be a no-op', () => { 202 | expectThrowsInProduction(PropTypes.oneOf('red', 'blue'), 'red'); 203 | expectThrowsInProduction(PropTypes.oneOf(['red', 'blue']), true); 204 | expectThrowsInProduction(PropTypes.oneOf(['red', 'blue']), null); 205 | expectThrowsInProduction(PropTypes.oneOf(['red', 'blue']), undefined); 206 | }); 207 | }); 208 | 209 | describe('Union Types', () => { 210 | it('should be a no-op', () => { 211 | expectThrowsInProduction( 212 | PropTypes.oneOfType(PropTypes.string, PropTypes.number), 213 | 'red', 214 | ); 215 | expectThrowsInProduction( 216 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 217 | [], 218 | ); 219 | expectThrowsInProduction( 220 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 221 | null, 222 | ); 223 | expectThrowsInProduction( 224 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 225 | undefined, 226 | ); 227 | }); 228 | }); 229 | 230 | describe('Shape Types', () => { 231 | it('should be a no-op', () => { 232 | expectThrowsInProduction(PropTypes.shape({}), 'some string'); 233 | expectThrowsInProduction( 234 | PropTypes.shape({key: PropTypes.number}).isRequired, 235 | null, 236 | ); 237 | expectThrowsInProduction( 238 | PropTypes.shape({key: PropTypes.number}).isRequired, 239 | undefined, 240 | ); 241 | }); 242 | }); 243 | 244 | describe('Exact Types', () => { 245 | it('should warn for non objects', () => { 246 | spyOn(console, 'error'); 247 | expectThrowsInProduction( 248 | PropTypes.exact({}), 249 | 'some string' 250 | ); 251 | expectThrowsInProduction( 252 | PropTypes.exact({}), 253 | ['array'] 254 | ); 255 | }); 256 | 257 | it('should not warn for empty values', () => { 258 | typeCheckPass(PropTypes.exact({}), undefined); 259 | typeCheckPass(PropTypes.exact({}), null); 260 | typeCheckPass(PropTypes.exact({}), {}); 261 | }); 262 | 263 | it('should not warn for an empty object', () => { 264 | typeCheckPass(PropTypes.exact({}).isRequired, {}); 265 | }); 266 | 267 | it('should warn for non specified types', () => { 268 | expectThrowsInProduction( 269 | PropTypes.exact({}), 270 | {key: 1}, 271 | 'Warning: Failed prop type: Invalid prop `testProp` key `key` supplied to `testComponent`.' + 272 | '\nBad object: {' + 273 | '\n \"key\": 1' + 274 | '\n}' + 275 | '\nValid keys: []' 276 | ); 277 | }); 278 | 279 | it('should not warn for valid types', () => { 280 | typeCheckPass(PropTypes.exact({key: PropTypes.number}), {key: 1}); 281 | }); 282 | 283 | it('should warn for required valid types', () => { 284 | expectThrowsInProduction( 285 | PropTypes.exact({key: PropTypes.number.isRequired}), 286 | {}, 287 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 288 | 'but its value is `undefined`.', 289 | ); 290 | }); 291 | 292 | it('should warn for the first required type', () => { 293 | expectThrowsInProduction( 294 | PropTypes.exact({ 295 | key: PropTypes.number.isRequired, 296 | secondKey: PropTypes.number.isRequired, 297 | }), 298 | {}, 299 | 'The prop `testProp.key` is marked as required in `testComponent`, ' + 300 | 'but its value is `undefined`.', 301 | ); 302 | }); 303 | 304 | it('should warn for invalid key types', () => { 305 | expectThrowsInProduction( 306 | PropTypes.exact({key: PropTypes.number}), 307 | {key: 'abc'}, 308 | 'Invalid prop `testProp.key` of type `string` supplied to `testComponent`, ' + 309 | 'expected `number`.', 310 | ); 311 | }); 312 | 313 | it('should be implicitly optional and not warn without values', () => { 314 | typeCheckPass( 315 | PropTypes.exact(PropTypes.exact({key: PropTypes.number})), 316 | null, 317 | ); 318 | typeCheckPass( 319 | PropTypes.exact(PropTypes.exact({key: PropTypes.number})), 320 | undefined, 321 | ); 322 | }); 323 | 324 | it('should warn for missing required values', () => { 325 | expectThrowsInProduction( 326 | PropTypes.exact({key: PropTypes.number}).isRequired, 327 | ); 328 | }); 329 | 330 | it('works with oneOfType', () => { 331 | typeCheckPass( 332 | PropTypes.exact({ foo: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }), 333 | { foo: 42 } 334 | ); 335 | typeCheckPass( 336 | PropTypes.exact({ foo: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }), 337 | { foo: '42' } 338 | ); 339 | expectThrowsInProduction( 340 | PropTypes.exact({ foo: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }), 341 | { foo: 42, bar: 'what is 6 * 7' }, 342 | `Warning: Failed prop type: Invalid prop \`testProp\` key \`bar\` supplied to \`testComponent\`. 343 | Bad object: { 344 | "foo": 42, 345 | "bar": "what is 6 * 7" 346 | } 347 | Valid keys: [ 348 | "foo" 349 | ]` 350 | ); 351 | }); 352 | 353 | it('works with a custom propType', () => { 354 | expectThrowsInProduction( 355 | PropTypes.oneOfType([() => new Error('hi')]), 356 | {}, 357 | 'Warning: Failed prop type: Invalid prop `testProp` supplied to `testComponent`.' 358 | ) 359 | }); 360 | }); 361 | 362 | describe('Symbol Type', () => { 363 | it('should warn for non-symbol', () => { 364 | expectThrowsInProduction( 365 | PropTypes.symbol, 366 | 'hello', 367 | 'Invalid prop `testProp` of type `string` supplied to ' + 368 | '`testComponent`, expected `symbol`.', 369 | ); 370 | expectThrowsInProduction( 371 | PropTypes.symbol, 372 | function() {}, 373 | 'Invalid prop `testProp` of type `function` supplied to ' + 374 | '`testComponent`, expected `symbol`.', 375 | ); 376 | expectThrowsInProduction( 377 | PropTypes.symbol, 378 | { 379 | '@@toStringTag': 'Katana', 380 | }, 381 | 'Invalid prop `testProp` of type `object` supplied to ' + 382 | '`testComponent`, expected `symbol`.', 383 | ); 384 | }); 385 | 386 | it('should not warn for a polyfilled Symbol', () => { 387 | const CoreSymbol = require('core-js/library/es6/symbol'); 388 | typeCheckPass(PropTypes.symbol, CoreSymbol('core-js')); 389 | }); 390 | }); 391 | 392 | describe('checkPropTypes', () => { 393 | it('does not call validators', () => { 394 | spyOn(console, 'error'); 395 | 396 | const spy = jest.fn(); 397 | typeCheckPass(PropTypes.string, 42); 398 | typeCheckPass(PropTypes.bool, 'whatever'); 399 | typeCheckPass(spy, 'no way'); 400 | expect(spy).not.toBeCalled(); 401 | }); 402 | 403 | describe('checkPropTypes.resetWarningCache', () => { 404 | it('should provide empty function', () => { 405 | spyOn(console, 'error'); 406 | 407 | var spy = jest.fn(); 408 | PropTypes.checkPropTypes.resetWarningCache(); 409 | expect(spy).not.toBeCalled(); 410 | }); 411 | }); 412 | }); 413 | 414 | describe('resetWarningCache', () => { 415 | it('should provide empty function', () => { 416 | spyOn(console, 'error'); 417 | 418 | var spy = jest.fn(); 419 | PropTypes.resetWarningCache(); 420 | expect(spy).not.toBeCalled(); 421 | }); 422 | }); 423 | }); 424 | -------------------------------------------------------------------------------- /checkPropTypes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var printWarning = function() {}; 11 | 12 | if (process.env.NODE_ENV !== 'production') { 13 | var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret'); 14 | var loggedTypeFailures = {}; 15 | var has = require('./lib/has'); 16 | 17 | printWarning = function(text) { 18 | var message = 'Warning: ' + text; 19 | if (typeof console !== 'undefined') { 20 | console.error(message); 21 | } 22 | try { 23 | // --- Welcome to debugging React --- 24 | // This error was thrown as a convenience so that you can use this stack 25 | // to find the callsite that caused this warning to fire. 26 | throw new Error(message); 27 | } catch (x) { /**/ } 28 | }; 29 | } 30 | 31 | /** 32 | * Assert that the values match with the type specs. 33 | * Error messages are memorized and will only be shown once. 34 | * 35 | * @param {object} typeSpecs Map of name to a ReactPropType 36 | * @param {object} values Runtime values that need to be type-checked 37 | * @param {string} location e.g. "prop", "context", "child context" 38 | * @param {string} componentName Name of the component for error messages. 39 | * @param {?Function} getStack Returns the component stack. 40 | * @private 41 | */ 42 | function checkPropTypes(typeSpecs, values, location, componentName, getStack) { 43 | if (process.env.NODE_ENV !== 'production') { 44 | for (var typeSpecName in typeSpecs) { 45 | if (has(typeSpecs, typeSpecName)) { 46 | var error; 47 | // Prop type validation may throw. In case they do, we don't want to 48 | // fail the render phase where it didn't fail before. So we log it. 49 | // After these have been cleaned up, we'll let them throw. 50 | try { 51 | // This is intentionally an invariant that gets caught. It's the same 52 | // behavior as without this statement except with a better message. 53 | if (typeof typeSpecs[typeSpecName] !== 'function') { 54 | var err = Error( 55 | (componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + 56 | 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + 57 | 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.' 58 | ); 59 | err.name = 'Invariant Violation'; 60 | throw err; 61 | } 62 | error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret); 63 | } catch (ex) { 64 | error = ex; 65 | } 66 | if (error && !(error instanceof Error)) { 67 | printWarning( 68 | (componentName || 'React class') + ': type specification of ' + 69 | location + ' `' + typeSpecName + '` is invalid; the type checker ' + 70 | 'function must return `null` or an `Error` but returned a ' + typeof error + '. ' + 71 | 'You may have forgotten to pass an argument to the type checker ' + 72 | 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 73 | 'shape all require an argument).' 74 | ); 75 | } 76 | if (error instanceof Error && !(error.message in loggedTypeFailures)) { 77 | // Only monitor this failure once because there tends to be a lot of the 78 | // same error. 79 | loggedTypeFailures[error.message] = true; 80 | 81 | var stack = getStack ? getStack() : ''; 82 | 83 | printWarning( 84 | 'Failed ' + location + ' type: ' + error.message + (stack != null ? stack : '') 85 | ); 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | /** 93 | * Resets warning cache when testing. 94 | * 95 | * @private 96 | */ 97 | checkPropTypes.resetWarningCache = function() { 98 | if (process.env.NODE_ENV !== 'production') { 99 | loggedTypeFailures = {}; 100 | } 101 | } 102 | 103 | module.exports = checkPropTypes; 104 | -------------------------------------------------------------------------------- /factory.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | // React 15.5 references this module, and assumes PropTypes are still callable in production. 11 | // Therefore we re-export development-only version with all the PropTypes checks here. 12 | // However if one is migrating to the `prop-types` npm library, they will go through the 13 | // `index.js` entry point, and it will branch depending on the environment. 14 | var factory = require('./factoryWithTypeCheckers'); 15 | module.exports = function(isValidElement) { 16 | // It is still allowed in 15.5. 17 | var throwOnDirectAccess = false; 18 | return factory(isValidElement, throwOnDirectAccess); 19 | }; 20 | -------------------------------------------------------------------------------- /factoryWithThrowingShims.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret'); 11 | 12 | function emptyFunction() {} 13 | function emptyFunctionWithReset() {} 14 | emptyFunctionWithReset.resetWarningCache = emptyFunction; 15 | 16 | module.exports = function() { 17 | function shim(props, propName, componentName, location, propFullName, secret) { 18 | if (secret === ReactPropTypesSecret) { 19 | // It is still safe when called from React. 20 | return; 21 | } 22 | var err = new Error( 23 | 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' + 24 | 'Use PropTypes.checkPropTypes() to call them. ' + 25 | 'Read more at http://fb.me/use-check-prop-types' 26 | ); 27 | err.name = 'Invariant Violation'; 28 | throw err; 29 | }; 30 | shim.isRequired = shim; 31 | function getShim() { 32 | return shim; 33 | }; 34 | // Important! 35 | // Keep this list in sync with production version in `./factoryWithTypeCheckers.js`. 36 | var ReactPropTypes = { 37 | array: shim, 38 | bigint: shim, 39 | bool: shim, 40 | func: shim, 41 | number: shim, 42 | object: shim, 43 | string: shim, 44 | symbol: shim, 45 | 46 | any: shim, 47 | arrayOf: getShim, 48 | element: shim, 49 | elementType: shim, 50 | instanceOf: getShim, 51 | node: shim, 52 | objectOf: getShim, 53 | oneOf: getShim, 54 | oneOfType: getShim, 55 | shape: getShim, 56 | exact: getShim, 57 | 58 | checkPropTypes: emptyFunctionWithReset, 59 | resetWarningCache: emptyFunction 60 | }; 61 | 62 | ReactPropTypes.PropTypes = ReactPropTypes; 63 | 64 | return ReactPropTypes; 65 | }; 66 | -------------------------------------------------------------------------------- /factoryWithTypeCheckers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var ReactIs = require('react-is'); 11 | var assign = require('object-assign'); 12 | 13 | var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret'); 14 | var has = require('./lib/has'); 15 | var checkPropTypes = require('./checkPropTypes'); 16 | 17 | var printWarning = function() {}; 18 | 19 | if (process.env.NODE_ENV !== 'production') { 20 | printWarning = function(text) { 21 | var message = 'Warning: ' + text; 22 | if (typeof console !== 'undefined') { 23 | console.error(message); 24 | } 25 | try { 26 | // --- Welcome to debugging React --- 27 | // This error was thrown as a convenience so that you can use this stack 28 | // to find the callsite that caused this warning to fire. 29 | throw new Error(message); 30 | } catch (x) {} 31 | }; 32 | } 33 | 34 | function emptyFunctionThatReturnsNull() { 35 | return null; 36 | } 37 | 38 | module.exports = function(isValidElement, throwOnDirectAccess) { 39 | /* global Symbol */ 40 | var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator; 41 | var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec. 42 | 43 | /** 44 | * Returns the iterator method function contained on the iterable object. 45 | * 46 | * Be sure to invoke the function with the iterable as context: 47 | * 48 | * var iteratorFn = getIteratorFn(myIterable); 49 | * if (iteratorFn) { 50 | * var iterator = iteratorFn.call(myIterable); 51 | * ... 52 | * } 53 | * 54 | * @param {?object} maybeIterable 55 | * @return {?function} 56 | */ 57 | function getIteratorFn(maybeIterable) { 58 | var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]); 59 | if (typeof iteratorFn === 'function') { 60 | return iteratorFn; 61 | } 62 | } 63 | 64 | /** 65 | * Collection of methods that allow declaration and validation of props that are 66 | * supplied to React components. Example usage: 67 | * 68 | * var Props = require('ReactPropTypes'); 69 | * var MyArticle = React.createClass({ 70 | * propTypes: { 71 | * // An optional string prop named "description". 72 | * description: Props.string, 73 | * 74 | * // A required enum prop named "category". 75 | * category: Props.oneOf(['News','Photos']).isRequired, 76 | * 77 | * // A prop named "dialog" that requires an instance of Dialog. 78 | * dialog: Props.instanceOf(Dialog).isRequired 79 | * }, 80 | * render: function() { ... } 81 | * }); 82 | * 83 | * A more formal specification of how these methods are used: 84 | * 85 | * type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...) 86 | * decl := ReactPropTypes.{type}(.isRequired)? 87 | * 88 | * Each and every declaration produces a function with the same signature. This 89 | * allows the creation of custom validation functions. For example: 90 | * 91 | * var MyLink = React.createClass({ 92 | * propTypes: { 93 | * // An optional string or URI prop named "href". 94 | * href: function(props, propName, componentName) { 95 | * var propValue = props[propName]; 96 | * if (propValue != null && typeof propValue !== 'string' && 97 | * !(propValue instanceof URI)) { 98 | * return new Error( 99 | * 'Expected a string or an URI for ' + propName + ' in ' + 100 | * componentName 101 | * ); 102 | * } 103 | * } 104 | * }, 105 | * render: function() {...} 106 | * }); 107 | * 108 | * @internal 109 | */ 110 | 111 | var ANONYMOUS = '<>'; 112 | 113 | // Important! 114 | // Keep this list in sync with production version in `./factoryWithThrowingShims.js`. 115 | var ReactPropTypes = { 116 | array: createPrimitiveTypeChecker('array'), 117 | bigint: createPrimitiveTypeChecker('bigint'), 118 | bool: createPrimitiveTypeChecker('boolean'), 119 | func: createPrimitiveTypeChecker('function'), 120 | number: createPrimitiveTypeChecker('number'), 121 | object: createPrimitiveTypeChecker('object'), 122 | string: createPrimitiveTypeChecker('string'), 123 | symbol: createPrimitiveTypeChecker('symbol'), 124 | 125 | any: createAnyTypeChecker(), 126 | arrayOf: createArrayOfTypeChecker, 127 | element: createElementTypeChecker(), 128 | elementType: createElementTypeTypeChecker(), 129 | instanceOf: createInstanceTypeChecker, 130 | node: createNodeChecker(), 131 | objectOf: createObjectOfTypeChecker, 132 | oneOf: createEnumTypeChecker, 133 | oneOfType: createUnionTypeChecker, 134 | shape: createShapeTypeChecker, 135 | exact: createStrictShapeTypeChecker, 136 | }; 137 | 138 | /** 139 | * inlined Object.is polyfill to avoid requiring consumers ship their own 140 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is 141 | */ 142 | /*eslint-disable no-self-compare*/ 143 | function is(x, y) { 144 | // SameValue algorithm 145 | if (x === y) { 146 | // Steps 1-5, 7-10 147 | // Steps 6.b-6.e: +0 != -0 148 | return x !== 0 || 1 / x === 1 / y; 149 | } else { 150 | // Step 6.a: NaN == NaN 151 | return x !== x && y !== y; 152 | } 153 | } 154 | /*eslint-enable no-self-compare*/ 155 | 156 | /** 157 | * We use an Error-like object for backward compatibility as people may call 158 | * PropTypes directly and inspect their output. However, we don't use real 159 | * Errors anymore. We don't inspect their stack anyway, and creating them 160 | * is prohibitively expensive if they are created too often, such as what 161 | * happens in oneOfType() for any type before the one that matched. 162 | */ 163 | function PropTypeError(message, data) { 164 | this.message = message; 165 | this.data = data && typeof data === 'object' ? data: {}; 166 | this.stack = ''; 167 | } 168 | // Make `instanceof Error` still work for returned errors. 169 | PropTypeError.prototype = Error.prototype; 170 | 171 | function createChainableTypeChecker(validate) { 172 | if (process.env.NODE_ENV !== 'production') { 173 | var manualPropTypeCallCache = {}; 174 | var manualPropTypeWarningCount = 0; 175 | } 176 | function checkType(isRequired, props, propName, componentName, location, propFullName, secret) { 177 | componentName = componentName || ANONYMOUS; 178 | propFullName = propFullName || propName; 179 | 180 | if (secret !== ReactPropTypesSecret) { 181 | if (throwOnDirectAccess) { 182 | // New behavior only for users of `prop-types` package 183 | var err = new Error( 184 | 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' + 185 | 'Use `PropTypes.checkPropTypes()` to call them. ' + 186 | 'Read more at http://fb.me/use-check-prop-types' 187 | ); 188 | err.name = 'Invariant Violation'; 189 | throw err; 190 | } else if (process.env.NODE_ENV !== 'production' && typeof console !== 'undefined') { 191 | // Old behavior for people using React.PropTypes 192 | var cacheKey = componentName + ':' + propName; 193 | if ( 194 | !manualPropTypeCallCache[cacheKey] && 195 | // Avoid spamming the console because they are often not actionable except for lib authors 196 | manualPropTypeWarningCount < 3 197 | ) { 198 | printWarning( 199 | 'You are manually calling a React.PropTypes validation ' + 200 | 'function for the `' + propFullName + '` prop on `' + componentName + '`. This is deprecated ' + 201 | 'and will throw in the standalone `prop-types` package. ' + 202 | 'You may be seeing this warning due to a third-party PropTypes ' + 203 | 'library. See https://fb.me/react-warning-dont-call-proptypes ' + 'for details.' 204 | ); 205 | manualPropTypeCallCache[cacheKey] = true; 206 | manualPropTypeWarningCount++; 207 | } 208 | } 209 | } 210 | if (props[propName] == null) { 211 | if (isRequired) { 212 | if (props[propName] === null) { 213 | return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.')); 214 | } 215 | return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.')); 216 | } 217 | return null; 218 | } else { 219 | return validate(props, propName, componentName, location, propFullName); 220 | } 221 | } 222 | 223 | var chainedCheckType = checkType.bind(null, false); 224 | chainedCheckType.isRequired = checkType.bind(null, true); 225 | 226 | return chainedCheckType; 227 | } 228 | 229 | function createPrimitiveTypeChecker(expectedType) { 230 | function validate(props, propName, componentName, location, propFullName, secret) { 231 | var propValue = props[propName]; 232 | var propType = getPropType(propValue); 233 | if (propType !== expectedType) { 234 | // `propValue` being instance of, say, date/regexp, pass the 'object' 235 | // check, but we can offer a more precise error message here rather than 236 | // 'of type `object`'. 237 | var preciseType = getPreciseType(propValue); 238 | 239 | return new PropTypeError( 240 | 'Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + preciseType + '` supplied to `' + componentName + '`, expected ') + ('`' + expectedType + '`.'), 241 | {expectedType: expectedType} 242 | ); 243 | } 244 | return null; 245 | } 246 | return createChainableTypeChecker(validate); 247 | } 248 | 249 | function createAnyTypeChecker() { 250 | return createChainableTypeChecker(emptyFunctionThatReturnsNull); 251 | } 252 | 253 | function createArrayOfTypeChecker(typeChecker) { 254 | function validate(props, propName, componentName, location, propFullName) { 255 | if (typeof typeChecker !== 'function') { 256 | return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside arrayOf.'); 257 | } 258 | var propValue = props[propName]; 259 | if (!Array.isArray(propValue)) { 260 | var propType = getPropType(propValue); 261 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an array.')); 262 | } 263 | for (var i = 0; i < propValue.length; i++) { 264 | var error = typeChecker(propValue, i, componentName, location, propFullName + '[' + i + ']', ReactPropTypesSecret); 265 | if (error instanceof Error) { 266 | return error; 267 | } 268 | } 269 | return null; 270 | } 271 | return createChainableTypeChecker(validate); 272 | } 273 | 274 | function createElementTypeChecker() { 275 | function validate(props, propName, componentName, location, propFullName) { 276 | var propValue = props[propName]; 277 | if (!isValidElement(propValue)) { 278 | var propType = getPropType(propValue); 279 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement.')); 280 | } 281 | return null; 282 | } 283 | return createChainableTypeChecker(validate); 284 | } 285 | 286 | function createElementTypeTypeChecker() { 287 | function validate(props, propName, componentName, location, propFullName) { 288 | var propValue = props[propName]; 289 | if (!ReactIs.isValidElementType(propValue)) { 290 | var propType = getPropType(propValue); 291 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement type.')); 292 | } 293 | return null; 294 | } 295 | return createChainableTypeChecker(validate); 296 | } 297 | 298 | function createInstanceTypeChecker(expectedClass) { 299 | function validate(props, propName, componentName, location, propFullName) { 300 | if (!(props[propName] instanceof expectedClass)) { 301 | var expectedClassName = expectedClass.name || ANONYMOUS; 302 | var actualClassName = getClassName(props[propName]); 303 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + actualClassName + '` supplied to `' + componentName + '`, expected ') + ('instance of `' + expectedClassName + '`.')); 304 | } 305 | return null; 306 | } 307 | return createChainableTypeChecker(validate); 308 | } 309 | 310 | function createEnumTypeChecker(expectedValues) { 311 | if (!Array.isArray(expectedValues)) { 312 | if (process.env.NODE_ENV !== 'production') { 313 | if (arguments.length > 1) { 314 | printWarning( 315 | 'Invalid arguments supplied to oneOf, expected an array, got ' + arguments.length + ' arguments. ' + 316 | 'A common mistake is to write oneOf(x, y, z) instead of oneOf([x, y, z]).' 317 | ); 318 | } else { 319 | printWarning('Invalid argument supplied to oneOf, expected an array.'); 320 | } 321 | } 322 | return emptyFunctionThatReturnsNull; 323 | } 324 | 325 | function validate(props, propName, componentName, location, propFullName) { 326 | var propValue = props[propName]; 327 | for (var i = 0; i < expectedValues.length; i++) { 328 | if (is(propValue, expectedValues[i])) { 329 | return null; 330 | } 331 | } 332 | 333 | var valuesString = JSON.stringify(expectedValues, function replacer(key, value) { 334 | var type = getPreciseType(value); 335 | if (type === 'symbol') { 336 | return String(value); 337 | } 338 | return value; 339 | }); 340 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of value `' + String(propValue) + '` ' + ('supplied to `' + componentName + '`, expected one of ' + valuesString + '.')); 341 | } 342 | return createChainableTypeChecker(validate); 343 | } 344 | 345 | function createObjectOfTypeChecker(typeChecker) { 346 | function validate(props, propName, componentName, location, propFullName) { 347 | if (typeof typeChecker !== 'function') { 348 | return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside objectOf.'); 349 | } 350 | var propValue = props[propName]; 351 | var propType = getPropType(propValue); 352 | if (propType !== 'object') { 353 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an object.')); 354 | } 355 | for (var key in propValue) { 356 | if (has(propValue, key)) { 357 | var error = typeChecker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret); 358 | if (error instanceof Error) { 359 | return error; 360 | } 361 | } 362 | } 363 | return null; 364 | } 365 | return createChainableTypeChecker(validate); 366 | } 367 | 368 | function createUnionTypeChecker(arrayOfTypeCheckers) { 369 | if (!Array.isArray(arrayOfTypeCheckers)) { 370 | process.env.NODE_ENV !== 'production' ? printWarning('Invalid argument supplied to oneOfType, expected an instance of array.') : void 0; 371 | return emptyFunctionThatReturnsNull; 372 | } 373 | 374 | for (var i = 0; i < arrayOfTypeCheckers.length; i++) { 375 | var checker = arrayOfTypeCheckers[i]; 376 | if (typeof checker !== 'function') { 377 | printWarning( 378 | 'Invalid argument supplied to oneOfType. Expected an array of check functions, but ' + 379 | 'received ' + getPostfixForTypeWarning(checker) + ' at index ' + i + '.' 380 | ); 381 | return emptyFunctionThatReturnsNull; 382 | } 383 | } 384 | 385 | function validate(props, propName, componentName, location, propFullName) { 386 | var expectedTypes = []; 387 | for (var i = 0; i < arrayOfTypeCheckers.length; i++) { 388 | var checker = arrayOfTypeCheckers[i]; 389 | var checkerResult = checker(props, propName, componentName, location, propFullName, ReactPropTypesSecret); 390 | if (checkerResult == null) { 391 | return null; 392 | } 393 | if (checkerResult.data && has(checkerResult.data, 'expectedType')) { 394 | expectedTypes.push(checkerResult.data.expectedType); 395 | } 396 | } 397 | var expectedTypesMessage = (expectedTypes.length > 0) ? ', expected one of type [' + expectedTypes.join(', ') + ']': ''; 398 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`' + expectedTypesMessage + '.')); 399 | } 400 | return createChainableTypeChecker(validate); 401 | } 402 | 403 | function createNodeChecker() { 404 | function validate(props, propName, componentName, location, propFullName) { 405 | if (!isNode(props[propName])) { 406 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a ReactNode.')); 407 | } 408 | return null; 409 | } 410 | return createChainableTypeChecker(validate); 411 | } 412 | 413 | function invalidValidatorError(componentName, location, propFullName, key, type) { 414 | return new PropTypeError( 415 | (componentName || 'React class') + ': ' + location + ' type `' + propFullName + '.' + key + '` is invalid; ' + 416 | 'it must be a function, usually from the `prop-types` package, but received `' + type + '`.' 417 | ); 418 | } 419 | 420 | function createShapeTypeChecker(shapeTypes) { 421 | function validate(props, propName, componentName, location, propFullName) { 422 | var propValue = props[propName]; 423 | var propType = getPropType(propValue); 424 | if (propType !== 'object') { 425 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.')); 426 | } 427 | for (var key in shapeTypes) { 428 | var checker = shapeTypes[key]; 429 | if (typeof checker !== 'function') { 430 | return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker)); 431 | } 432 | var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret); 433 | if (error) { 434 | return error; 435 | } 436 | } 437 | return null; 438 | } 439 | return createChainableTypeChecker(validate); 440 | } 441 | 442 | function createStrictShapeTypeChecker(shapeTypes) { 443 | function validate(props, propName, componentName, location, propFullName) { 444 | var propValue = props[propName]; 445 | var propType = getPropType(propValue); 446 | if (propType !== 'object') { 447 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.')); 448 | } 449 | // We need to check all keys in case some are required but missing from props. 450 | var allKeys = assign({}, props[propName], shapeTypes); 451 | for (var key in allKeys) { 452 | var checker = shapeTypes[key]; 453 | if (has(shapeTypes, key) && typeof checker !== 'function') { 454 | return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker)); 455 | } 456 | if (!checker) { 457 | return new PropTypeError( 458 | 'Invalid ' + location + ' `' + propFullName + '` key `' + key + '` supplied to `' + componentName + '`.' + 459 | '\nBad object: ' + JSON.stringify(props[propName], null, ' ') + 460 | '\nValid keys: ' + JSON.stringify(Object.keys(shapeTypes), null, ' ') 461 | ); 462 | } 463 | var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret); 464 | if (error) { 465 | return error; 466 | } 467 | } 468 | return null; 469 | } 470 | 471 | return createChainableTypeChecker(validate); 472 | } 473 | 474 | function isNode(propValue) { 475 | switch (typeof propValue) { 476 | case 'number': 477 | case 'string': 478 | case 'undefined': 479 | return true; 480 | case 'boolean': 481 | return !propValue; 482 | case 'object': 483 | if (Array.isArray(propValue)) { 484 | return propValue.every(isNode); 485 | } 486 | if (propValue === null || isValidElement(propValue)) { 487 | return true; 488 | } 489 | 490 | var iteratorFn = getIteratorFn(propValue); 491 | if (iteratorFn) { 492 | var iterator = iteratorFn.call(propValue); 493 | var step; 494 | if (iteratorFn !== propValue.entries) { 495 | while (!(step = iterator.next()).done) { 496 | if (!isNode(step.value)) { 497 | return false; 498 | } 499 | } 500 | } else { 501 | // Iterator will provide entry [k,v] tuples rather than values. 502 | while (!(step = iterator.next()).done) { 503 | var entry = step.value; 504 | if (entry) { 505 | if (!isNode(entry[1])) { 506 | return false; 507 | } 508 | } 509 | } 510 | } 511 | } else { 512 | return false; 513 | } 514 | 515 | return true; 516 | default: 517 | return false; 518 | } 519 | } 520 | 521 | function isSymbol(propType, propValue) { 522 | // Native Symbol. 523 | if (propType === 'symbol') { 524 | return true; 525 | } 526 | 527 | // falsy value can't be a Symbol 528 | if (!propValue) { 529 | return false; 530 | } 531 | 532 | // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol' 533 | if (propValue['@@toStringTag'] === 'Symbol') { 534 | return true; 535 | } 536 | 537 | // Fallback for non-spec compliant Symbols which are polyfilled. 538 | if (typeof Symbol === 'function' && propValue instanceof Symbol) { 539 | return true; 540 | } 541 | 542 | return false; 543 | } 544 | 545 | // Equivalent of `typeof` but with special handling for array and regexp. 546 | function getPropType(propValue) { 547 | var propType = typeof propValue; 548 | if (Array.isArray(propValue)) { 549 | return 'array'; 550 | } 551 | if (propValue instanceof RegExp) { 552 | // Old webkits (at least until Android 4.0) return 'function' rather than 553 | // 'object' for typeof a RegExp. We'll normalize this here so that /bla/ 554 | // passes PropTypes.object. 555 | return 'object'; 556 | } 557 | if (isSymbol(propType, propValue)) { 558 | return 'symbol'; 559 | } 560 | return propType; 561 | } 562 | 563 | // This handles more types than `getPropType`. Only used for error messages. 564 | // See `createPrimitiveTypeChecker`. 565 | function getPreciseType(propValue) { 566 | if (typeof propValue === 'undefined' || propValue === null) { 567 | return '' + propValue; 568 | } 569 | var propType = getPropType(propValue); 570 | if (propType === 'object') { 571 | if (propValue instanceof Date) { 572 | return 'date'; 573 | } else if (propValue instanceof RegExp) { 574 | return 'regexp'; 575 | } 576 | } 577 | return propType; 578 | } 579 | 580 | // Returns a string that is postfixed to a warning about an invalid type. 581 | // For example, "undefined" or "of type array" 582 | function getPostfixForTypeWarning(value) { 583 | var type = getPreciseType(value); 584 | switch (type) { 585 | case 'array': 586 | case 'object': 587 | return 'an ' + type; 588 | case 'boolean': 589 | case 'date': 590 | case 'regexp': 591 | return 'a ' + type; 592 | default: 593 | return type; 594 | } 595 | } 596 | 597 | // Returns class name of the object, if any. 598 | function getClassName(propValue) { 599 | if (!propValue.constructor || !propValue.constructor.name) { 600 | return ANONYMOUS; 601 | } 602 | return propValue.constructor.name; 603 | } 604 | 605 | ReactPropTypes.checkPropTypes = checkPropTypes; 606 | ReactPropTypes.resetWarningCache = checkPropTypes.resetWarningCache; 607 | ReactPropTypes.PropTypes = ReactPropTypes; 608 | 609 | return ReactPropTypes; 610 | }; 611 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | if (process.env.NODE_ENV !== 'production') { 9 | var ReactIs = require('react-is'); 10 | 11 | // By explicitly using `prop-types` you are opting into new development behavior. 12 | // http://fb.me/prop-types-in-prod 13 | var throwOnDirectAccess = true; 14 | module.exports = require('./factoryWithTypeCheckers')(ReactIs.isElement, throwOnDirectAccess); 15 | } else { 16 | // By explicitly using `prop-types` you are opting into new production behavior. 17 | // http://fb.me/prop-types-in-prod 18 | module.exports = require('./factoryWithThrowingShims')(); 19 | } 20 | -------------------------------------------------------------------------------- /lib/ReactPropTypesSecret.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'; 11 | 12 | module.exports = ReactPropTypesSecret; 13 | -------------------------------------------------------------------------------- /lib/has.js: -------------------------------------------------------------------------------- 1 | module.exports = Function.call.bind(Object.prototype.hasOwnProperty); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prop-types", 3 | "version": "15.8.1", 4 | "description": "Runtime type checking for React props and similar objects.", 5 | "sideEffects": false, 6 | "main": "index.js", 7 | "license": "MIT", 8 | "files": [ 9 | "LICENSE", 10 | "README.md", 11 | "checkPropTypes.js", 12 | "factory.js", 13 | "factoryWithThrowingShims.js", 14 | "factoryWithTypeCheckers.js", 15 | "index.js", 16 | "prop-types.js", 17 | "prop-types.min.js", 18 | "lib" 19 | ], 20 | "repository": "facebook/prop-types", 21 | "keywords": [ 22 | "react" 23 | ], 24 | "bugs": { 25 | "url": "https://github.com/facebook/prop-types/issues" 26 | }, 27 | "homepage": "https://facebook.github.io/react/", 28 | "dependencies": { 29 | "loose-envify": "^1.4.0", 30 | "object-assign": "^4.1.1", 31 | "react-is": "^16.13.1" 32 | }, 33 | "scripts": { 34 | "pretest": "npm run lint", 35 | "lint": "eslint .", 36 | "test": "npm run tests-only", 37 | "tests-only": "jest", 38 | "umd": "NODE_ENV=development browserify index.js -t loose-envify --standalone PropTypes -o prop-types.js", 39 | "umd-min": "NODE_ENV=production browserify index.js -t loose-envify -t uglifyify --standalone PropTypes -p bundle-collapser/plugin -o | uglifyjs --compress unused,dead_code -o prop-types.min.js", 40 | "build": "yarn umd && yarn umd-min", 41 | "prepublish": "not-in-publish || yarn build" 42 | }, 43 | "devDependencies": { 44 | "babel-jest": "^19.0.0", 45 | "babel-preset-react": "^6.24.1", 46 | "browserify": "^16.5.0", 47 | "bundle-collapser": "^1.4.0", 48 | "eslint": "^8.6.0", 49 | "in-publish": "^2.0.1", 50 | "jest": "^19.0.2", 51 | "react": "^15.7.0", 52 | "uglifyify": "^5.0.2", 53 | "uglifyjs": "^2.4.11" 54 | }, 55 | "browserify": { 56 | "transform": [ 57 | "loose-envify" 58 | ] 59 | } 60 | } 61 | --------------------------------------------------------------------------------