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
});