Skip to content

Commit 4329d5a

Browse files
committed
Merge pull request facebook#1611 from mathieumg/master
Added 'objectOf' PropType validator to iterate on objects and validate properties.
2 parents 7ab5769 + 4852c30 commit 4329d5a

File tree

2 files changed

+121
-2
lines changed

2 files changed

+121
-2
lines changed

src/core/ReactPropTypes.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ var ReactPropTypes = {
8383
arrayOf: createArrayOfTypeChecker,
8484
component: createComponentTypeChecker(),
8585
instanceOf: createInstanceTypeChecker,
86+
objectOf: createObjectOfTypeChecker,
8687
oneOf: createEnumTypeChecker,
8788
oneOfType: createUnionTypeChecker,
8889
renderable: createRenderableTypeChecker(),
@@ -202,6 +203,29 @@ function createEnumTypeChecker(expectedValues) {
202203
return createChainableTypeChecker(validate);
203204
}
204205

206+
function createObjectOfTypeChecker(typeChecker) {
207+
function validate(props, propName, componentName, location) {
208+
var propValue = props[propName];
209+
var propType = getPropType(propValue);
210+
if (propType !== 'object') {
211+
var locationName = ReactPropTypeLocationNames[location];
212+
return new Error(
213+
`Invalid ${locationName} \`${propName}\` of type ` +
214+
`\`${propType}\` supplied to \`${componentName}\`, expected an object.`
215+
);
216+
}
217+
for (var key in propValue) {
218+
if (propValue.hasOwnProperty(key)) {
219+
var error = typeChecker(propValue, key, componentName, location);
220+
if (error instanceof Error) {
221+
return error;
222+
}
223+
}
224+
}
225+
}
226+
return createChainableTypeChecker(validate);
227+
}
228+
205229
function createUnionTypeChecker(arrayOfTypeCheckers) {
206230
function validate(props, propName, componentName, location) {
207231
for (var i = 0; i < arrayOfTypeCheckers.length; i++) {

src/core/__tests__/ReactPropTypes-test.js

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,97 @@ describe('React Component Types', function() {
406406
});
407407
});
408408

409+
describe('ObjectOf Type', function() {
410+
it('should support the objectOf propTypes', function() {
411+
typeCheckPass(PropTypes.objectOf(PropTypes.number), {a: 1, b: 2, c: 3});
412+
typeCheckPass(
413+
PropTypes.objectOf(PropTypes.string),
414+
{a: 'a', b: 'b', c: 'c'}
415+
);
416+
typeCheckPass(
417+
PropTypes.objectOf(PropTypes.oneOf(['a', 'b'])),
418+
{a: 'a', b: 'b'}
419+
);
420+
});
421+
422+
it('should support objectOf with complex types', function() {
423+
typeCheckPass(
424+
PropTypes.objectOf(PropTypes.shape({a: PropTypes.number.isRequired})),
425+
{a: {a: 1}, b: {a: 2}}
426+
);
427+
428+
function Thing() {}
429+
typeCheckPass(
430+
PropTypes.objectOf(PropTypes.instanceOf(Thing)),
431+
{a: new Thing(), b: new Thing()}
432+
);
433+
});
434+
435+
it('should warn with invalid items in the object', function() {
436+
typeCheckFail(
437+
PropTypes.objectOf(PropTypes.number),
438+
{a: 1, b: 2, c: 'b'},
439+
'Invalid prop `c` of type `string` supplied to `testComponent`, ' +
440+
'expected `number`.'
441+
);
442+
});
443+
444+
it('should warn with invalid complex types', function() {
445+
function Thing() {}
446+
var name = Thing.name || '<<anonymous>>';
447+
448+
typeCheckFail(
449+
PropTypes.objectOf(PropTypes.instanceOf(Thing)),
450+
{a: new Thing(), b: 'xyz'},
451+
'Invalid prop `b` supplied to `testComponent`, expected instance of `' +
452+
name + '`.'
453+
);
454+
});
455+
456+
it('should warn when passed something other than an object', function() {
457+
typeCheckFail(
458+
PropTypes.objectOf(PropTypes.number),
459+
[1, 2],
460+
'Invalid prop `testProp` of type `array` supplied to `testComponent`, ' +
461+
'expected an object.'
462+
);
463+
typeCheckFail(
464+
PropTypes.objectOf(PropTypes.number),
465+
123,
466+
'Invalid prop `testProp` of type `number` supplied to `testComponent`, ' +
467+
'expected an object.'
468+
);
469+
typeCheckFail(
470+
PropTypes.objectOf(PropTypes.number),
471+
'string',
472+
'Invalid prop `testProp` of type `string` supplied to `testComponent`, ' +
473+
'expected an object.'
474+
);
475+
});
476+
477+
it('should not warn when passing an empty object', function() {
478+
typeCheckPass(PropTypes.objectOf(PropTypes.number), {});
479+
});
480+
481+
it("should be implicitly optional and not warn without values", function() {
482+
typeCheckPass(PropTypes.objectOf(PropTypes.number), null);
483+
typeCheckPass(PropTypes.objectOf(PropTypes.number), undefined);
484+
});
485+
486+
it("should warn for missing required values", function() {
487+
typeCheckFail(
488+
PropTypes.objectOf(PropTypes.number).isRequired,
489+
null,
490+
requiredMessage
491+
);
492+
typeCheckFail(
493+
PropTypes.objectOf(PropTypes.number).isRequired,
494+
undefined,
495+
requiredMessage
496+
);
497+
});
498+
});
499+
409500
describe('OneOf Types', function() {
410501
it("should warn for invalid strings", function() {
411502
typeCheckFail(
@@ -446,10 +537,14 @@ describe('OneOf Types', function() {
446537

447538
it("should warn for missing required values", function() {
448539
typeCheckFail(
449-
PropTypes.oneOf(['red', 'blue']).isRequired, null, requiredMessage
540+
PropTypes.oneOf(['red', 'blue']).isRequired,
541+
null,
542+
requiredMessage
450543
);
451544
typeCheckFail(
452-
PropTypes.oneOf(['red', 'blue']).isRequired, undefined, requiredMessage
545+
PropTypes.oneOf(['red', 'blue']).isRequired,
546+
undefined,
547+
requiredMessage
453548
);
454549
});
455550
});

0 commit comments

Comments
 (0)