@samitouri / QOS-React-2 / commits / 12a43d4eee

Better warnings for nested propTypes

`arrayOf`, `shape` and `objectOf` warnings now display the full path of the invalid key.

Morhaus committed Apr 24, 2015 at 11:44 UTC 12a43d4eeee4edf0768c84a9aa393941dd48db8b
3 files changed +68 -39
src/addons/link/__tests__/ReactLinkPropTypes-test.js
+6 -6
@@ -48,22 +48,22 @@ describe('ReactLink', function() {
48 typeCheckFail(
49 LinkPropTypes.link(React.PropTypes.any),
50 {},
51 - 'Required prop `value` was not specified in `testComponent`.'
51 + 'Required prop `testProp.value` was not specified in `testComponent`.'
52 );
53 typeCheckFail(
54 LinkPropTypes.link(React.PropTypes.any),
55 {value: 123},
56 - 'Required prop `requestChange` was not specified in `testComponent`.'
56 + 'Required prop `testProp.requestChange` was not specified in `testComponent`.'
57 );
58 typeCheckFail(
59 LinkPropTypes.link(React.PropTypes.any),
60 {requestChange: emptyFunction},
61 - 'Required prop `value` was not specified in `testComponent`.'
61 + 'Required prop `testProp.value` was not specified in `testComponent`.'
62 );
63 typeCheckFail(
64 LinkPropTypes.link(React.PropTypes.any),
65 {value: null, requestChange: null},
66 - 'Required prop `value` was not specified in `testComponent`.'
66 + 'Required prop `testProp.value` was not specified in `testComponent`.'
67 );
68 });
69
@@ -104,7 +104,7 @@ describe('ReactLink', function() {
104 typeCheckFail(
105 LinkPropTypes.link(React.PropTypes.string),
106 {value: 123, requestChange: emptyFunction},
107 - 'Invalid prop `value` of type `number` supplied to `testComponent`,' +
107 + 'Invalid prop `testProp.value` of type `number` supplied to `testComponent`,' +
108 ' expected `string`.'
109 );
110 });
@@ -148,7 +148,7 @@ describe('ReactLink', function() {
148 typeCheckFail(
149 LinkPropTypes.link(React.PropTypes.oneOfType([React.PropTypes.number])),
150 {value: 'imastring', requestChange: emptyFunction},
151 - 'Invalid prop `value` supplied to `testComponent`.'
151 + 'Invalid prop `testProp.value` supplied to `testComponent`.'
152 );
153 });
154 });
src/classic/types/ReactPropTypes.js
+55 -26
@@ -86,19 +86,27 @@ var ReactPropTypes = {
86 };
87
88 function createChainableTypeChecker(validate) {
89 - function checkType(isRequired, props, propName, componentName, location) {
89 + function checkType(
90 + isRequired,
91 + props,
92 + propName,
93 + componentName,
94 + location,
95 + propFullName
96 + ) {
97 componentName = componentName || ANONYMOUS;
98 + propFullName = propFullName || propName;
99 if (props[propName] == null) {
100 var locationName = ReactPropTypeLocationNames[location];
101 if (isRequired) {
102 return new Error(
95 - `Required ${locationName} \`${propName}\` was not specified in ` +
103 + `Required ${locationName} \`${propFullName}\` was not specified in ` +
104 `\`${componentName}\`.`
105 );
106 }
107 return null;
108 } else {
101 - return validate(props, propName, componentName, location);
109 + return validate(props, propName, componentName, location, propFullName);
110 }
111 }
112
@@ -109,7 +117,7 @@ function createChainableTypeChecker(validate) {
117 }
118
119 function createPrimitiveTypeChecker(expectedType) {
112 - function validate(props, propName, componentName, location) {
120 + function validate(props, propName, componentName, location, propFullName) {
121 var propValue = props[propName];
122 var propType = getPropType(propValue);
123 if (propType !== expectedType) {
@@ -120,8 +128,9 @@ function createPrimitiveTypeChecker(expectedType) {
128 var preciseType = getPreciseType(propValue);
129
130 return new Error(
123 - `Invalid ${locationName} \`${propName}\` of type \`${preciseType}\` ` +
124 - `supplied to \`${componentName}\`, expected \`${expectedType}\`.`
131 + `Invalid ${locationName} \`${propFullName}\` of type ` +
132 + `\`${preciseType}\` supplied to \`${componentName}\`, expected ` +
133 + `\`${expectedType}\`.`
134 );
135 }
136 return null;
@@ -134,18 +143,24 @@ function createAnyTypeChecker() {
143 }
144
145 function createArrayOfTypeChecker(typeChecker) {
137 - function validate(props, propName, componentName, location) {
146 + function validate(props, propName, componentName, location, propFullName) {
147 var propValue = props[propName];
148 if (!Array.isArray(propValue)) {
149 var locationName = ReactPropTypeLocationNames[location];
150 var propType = getPropType(propValue);
151 return new Error(
143 - `Invalid ${locationName} \`${propName}\` of type ` +
152 + `Invalid ${locationName} \`${propFullName}\` of type ` +
153 `\`${propType}\` supplied to \`${componentName}\`, expected an array.`
154 );
155 }
156 for (var i = 0; i < propValue.length; i++) {
148 - var error = typeChecker(propValue, i, componentName, location);
157 + var error = typeChecker(
158 + propValue,
159 + i,
160 + componentName,
161 + location,
162 + `${propFullName}[${i}]`
163 + );
164 if (error instanceof Error) {
165 return error;
166 }
@@ -156,11 +171,11 @@ function createArrayOfTypeChecker(typeChecker) {
171 }
172
173 function createElementTypeChecker() {
159 - function validate(props, propName, componentName, location) {
174 + function validate(props, propName, componentName, location, propFullName) {
175 if (!ReactElement.isValidElement(props[propName])) {
176 var locationName = ReactPropTypeLocationNames[location];
177 return new Error(
163 - `Invalid ${locationName} \`${propName}\` supplied to ` +
178 + `Invalid ${locationName} \`${propFullName}\` supplied to ` +
179 `\`${componentName}\`, expected a single ReactElement.`
180 );
181 }
@@ -170,12 +185,12 @@ function createElementTypeChecker() {
185 }
186
187 function createInstanceTypeChecker(expectedClass) {
173 - function validate(props, propName, componentName, location) {
188 + function validate(props, propName, componentName, location, propFullName) {
189 if (!(props[propName] instanceof expectedClass)) {
190 var locationName = ReactPropTypeLocationNames[location];
191 var expectedClassName = expectedClass.name || ANONYMOUS;
192 return new Error(
178 - `Invalid ${locationName} \`${propName}\` supplied to ` +
193 + `Invalid ${locationName} \`${propFullName}\` supplied to ` +
194 `\`${componentName}\`, expected instance of \`${expectedClassName}\`.`
195 );
196 }
@@ -185,7 +200,7 @@ function createInstanceTypeChecker(expectedClass) {
200 }
201
202 function createEnumTypeChecker(expectedValues) {
188 - function validate(props, propName, componentName, location) {
203 + function validate(props, propName, componentName, location, propFullName) {
204 var propValue = props[propName];
205 for (var i = 0; i < expectedValues.length; i++) {
206 if (propValue === expectedValues[i]) {
@@ -196,7 +211,7 @@ function createEnumTypeChecker(expectedValues) {
211 var locationName = ReactPropTypeLocationNames[location];
212 var valuesString = JSON.stringify(expectedValues);
213 return new Error(
199 - `Invalid ${locationName} \`${propName}\` of value \`${propValue}\` ` +
214 + `Invalid ${locationName} \`${propFullName}\` of value \`${propValue}\` ` +
215 `supplied to \`${componentName}\`, expected one of ${valuesString}.`
216 );
217 }
@@ -204,19 +219,25 @@ function createEnumTypeChecker(expectedValues) {
219 }
220
221 function createObjectOfTypeChecker(typeChecker) {
207 - function validate(props, propName, componentName, location) {
222 + function validate(props, propName, componentName, location, propFullName) {
223 var propValue = props[propName];
224 var propType = getPropType(propValue);
225 if (propType !== 'object') {
226 var locationName = ReactPropTypeLocationNames[location];
227 return new Error(
213 - `Invalid ${locationName} \`${propName}\` of type ` +
228 + `Invalid ${locationName} \`${propFullName}\` of type ` +
229 `\`${propType}\` supplied to \`${componentName}\`, expected an object.`
230 );
231 }
232 for (var key in propValue) {
233 if (propValue.hasOwnProperty(key)) {
219 - var error = typeChecker(propValue, key, componentName, location);
234 + var error = typeChecker(
235 + propValue,
236 + key,
237 + componentName,
238 + location,
239 + `${propFullName}.${key}`
240 + );
241 if (error instanceof Error) {
242 return error;
243 }
@@ -228,17 +249,19 @@ function createObjectOfTypeChecker(typeChecker) {
249 }
250
251 function createUnionTypeChecker(arrayOfTypeCheckers) {
231 - function validate(props, propName, componentName, location) {
252 + function validate(props, propName, componentName, location, propFullName) {
253 for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
254 var checker = arrayOfTypeCheckers[i];
234 - if (checker(props, propName, componentName, location) == null) {
255 + if (
256 + checker(props, propName, componentName, location, propFullName) == null
257 + ) {
258 return null;
259 }
260 }
261
262 var locationName = ReactPropTypeLocationNames[location];
263 return new Error(
241 - `Invalid ${locationName} \`${propName}\` supplied to ` +
264 + `Invalid ${locationName} \`${propFullName}\` supplied to ` +
265 `\`${componentName}\`.`
266 );
267 }
@@ -246,11 +269,11 @@ function createUnionTypeChecker(arrayOfTypeCheckers) {
269 }
270
271 function createNodeChecker() {
249 - function validate(props, propName, componentName, location) {
272 + function validate(props, propName, componentName, location, propFullName) {
273 if (!isNode(props[propName])) {
274 var locationName = ReactPropTypeLocationNames[location];
275 return new Error(
253 - `Invalid ${locationName} \`${propName}\` supplied to ` +
276 + `Invalid ${locationName} \`${propFullName}\` supplied to ` +
277 `\`${componentName}\`, expected a ReactNode.`
278 );
279 }
@@ -260,13 +283,13 @@ function createNodeChecker() {
283 }
284
285 function createShapeTypeChecker(shapeTypes) {
263 - function validate(props, propName, componentName, location) {
286 + function validate(props, propName, componentName, location, propFullName) {
287 var propValue = props[propName];
288 var propType = getPropType(propValue);
289 if (propType !== 'object') {
290 var locationName = ReactPropTypeLocationNames[location];
291 return new Error(
269 - `Invalid ${locationName} \`${propName}\` of type \`${propType}\` ` +
292 + `Invalid ${locationName} \`${propFullName}\` of type \`${propType}\` ` +
293 `supplied to \`${componentName}\`, expected \`object\`.`
294 );
295 }
@@ -275,7 +298,13 @@ function createShapeTypeChecker(shapeTypes) {
298 if (!checker) {
299 continue;
300 }
278 - var error = checker(propValue, key, componentName, location);
301 + var error = checker(
302 + propValue,
303 + key,
304 + componentName,
305 + location,
306 + `${propFullName}.${key}`
307 + );
308 if (error) {
309 return error;
310 }
src/classic/types/__tests__/ReactPropTypes-test.js
+7 -7
@@ -161,7 +161,7 @@ describe('ReactPropTypes', function() {
161 typeCheckFail(
162 PropTypes.arrayOf(PropTypes.number),
163 [1, 2, 'b'],
164 - 'Invalid prop `2` of type `string` supplied to `testComponent`, ' +
164 + 'Invalid prop `testProp[2]` of type `string` supplied to `testComponent`, ' +
165 'expected `number`.'
166 );
167 });
@@ -173,7 +173,7 @@ describe('ReactPropTypes', function() {
173 typeCheckFail(
174 PropTypes.arrayOf(PropTypes.instanceOf(Thing)),
175 [new Thing(), 'xyz'],
176 - 'Invalid prop `1` supplied to `testComponent`, expected instance of `' +
176 + 'Invalid prop `testProp[1]` supplied to `testComponent`, expected instance of `' +
177 name + '`.'
178 );
179 });
@@ -458,7 +458,7 @@ describe('ReactPropTypes', function() {
458 typeCheckFail(
459 PropTypes.objectOf(PropTypes.number),
460 {a: 1, b: 2, c: 'b'},
461 - 'Invalid prop `c` of type `string` supplied to `testComponent`, ' +
461 + 'Invalid prop `testProp.c` of type `string` supplied to `testComponent`, ' +
462 'expected `number`.'
463 );
464 });
@@ -470,7 +470,7 @@ describe('ReactPropTypes', function() {
470 typeCheckFail(
471 PropTypes.objectOf(PropTypes.instanceOf(Thing)),
472 {a: new Thing(), b: 'xyz'},
473 - 'Invalid prop `b` supplied to `testComponent`, expected instance of `' +
473 + 'Invalid prop `testProp.b` supplied to `testComponent`, expected instance of `' +
474 name + '`.'
475 );
476 });
@@ -668,7 +668,7 @@ describe('ReactPropTypes', function() {
668 typeCheckFail(
669 PropTypes.shape({key: PropTypes.number.isRequired}),
670 {},
671 - 'Required prop `key` was not specified in `testComponent`.'
671 + 'Required prop `testProp.key` was not specified in `testComponent`.'
672 );
673 });
674
@@ -679,14 +679,14 @@ describe('ReactPropTypes', function() {
679 secondKey: PropTypes.number.isRequired
680 }),
681 {},
682 - 'Required prop `key` was not specified in `testComponent`.'
682 + 'Required prop `testProp.key` was not specified in `testComponent`.'
683 );
684 });
685
686 it("should warn for invalid key types", function() {
687 typeCheckFail(PropTypes.shape({key: PropTypes.number}),
688 {key: 'abc'},
689 - 'Invalid prop `key` of type `string` supplied to `testComponent`, ' +
689 + 'Invalid prop `testProp.key` of type `string` supplied to `testComponent`, ' +
690 'expected `number`.'
691 );
692 });