Skip to content
This repository has been archived by the owner on Sep 1, 2024. It is now read-only.

Add notNull modifier #59

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ MyComponent.propTypes = {
// A value of any data type
requiredAny: PropTypes.any.isRequired,

// You can use `notNull` like `isRequired`
// to allow undefined but not null
optionalObjectNotNull: PropTypes.object.notNull,

// You can also specify a custom validator. It should return an Error
// object if the validation fails. Don't `console.warn` or throw, as this
// won't work inside `oneOfType`.
Expand Down Expand Up @@ -142,18 +146,18 @@ For example:

```js
"dependencies": {
"prop-types": "^15.5.7"
"prop-types": "^15.5.7"
}
```

For libraries, we *also* recommend leaving it in `dependencies`:

```js
"dependencies": {
"prop-types": "^15.5.7"
"prop-types": "^15.5.7"
},
"peerDependencies": {
"react": "^15.5.0"
"react": "^15.5.0"
}
```

Expand Down
69 changes: 69 additions & 0 deletions __tests__/PropTypesDevelopmentStandalone-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,27 @@ function typeCheckPass(declaration, value) {
expect(message).toBe(null);
}

function typeCheckNullFail(declaration, value) {
const propTypes = {
testProp: declaration,
};
var mustNotBeNullMsg = 'The prop `testProp` in `testComponent`' +
' must not be `null`.';
const message1 = getPropTypeWarningMessage(
propTypes,
{testProp: null},
'testComponent'
);
expect(message1).toContain(mustNotBeNullMsg);

const message2 = getPropTypeWarningMessage(
propTypes,
{testProp: undefined},
'testComponent'
);
expect(message2).toBe(null);
}

function expectThrowsInDevelopment(declaration, value) {
resetWarningCache();
var props = {testProp: value};
Expand Down Expand Up @@ -214,6 +235,10 @@ describe('PropTypesDevelopmentStandalone', () => {
typeCheckPass(PropTypes.string, undefined);
});

it('should warn when null is not allowed', () => {
typeCheckNullFail(PropTypes.string.notNull);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(PropTypes.string.isRequired);
});
Expand Down Expand Up @@ -278,6 +303,10 @@ describe('PropTypesDevelopmentStandalone', () => {
typeCheckPass(PropTypes.any, undefined);
});

it('should warn when null is not allowed', () => {
typeCheckNullFail(PropTypes.any.notNull);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(PropTypes.any.isRequired);
});
Expand Down Expand Up @@ -372,6 +401,12 @@ describe('PropTypesDevelopmentStandalone', () => {
typeCheckPass(PropTypes.arrayOf(PropTypes.number), undefined);
});

it('should warn when null is not allowed', () => {
typeCheckNullFail(
PropTypes.arrayOf(PropTypes.number).notNull
);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(
PropTypes.arrayOf(PropTypes.number).isRequired,
Expand Down Expand Up @@ -441,6 +476,10 @@ describe('PropTypesDevelopmentStandalone', () => {
typeCheckPass(PropTypes.element, undefined);
});

it('should warn when null is not allowed', () => {
typeCheckNullFail(PropTypes.element.notNull);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(PropTypes.element.isRequired);
});
Expand Down Expand Up @@ -540,6 +579,10 @@ describe('PropTypesDevelopmentStandalone', () => {
typeCheckPass(PropTypes.instanceOf(String), undefined);
});

it('should warn when null is not allowed', () => {
typeCheckNullFail(PropTypes.instanceOf(String).notNull);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(PropTypes.instanceOf(String).isRequired);
});
Expand Down Expand Up @@ -636,6 +679,10 @@ describe('PropTypesDevelopmentStandalone', () => {
typeCheckPass(PropTypes.node, undefined);
});

it('should warn when null is not allowed', () => {
typeCheckNullFail(PropTypes.node.notNull);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(PropTypes.node.isRequired);
});
Expand Down Expand Up @@ -753,6 +800,12 @@ describe('PropTypesDevelopmentStandalone', () => {
typeCheckPass(PropTypes.objectOf(PropTypes.number), undefined);
});

it('should warn when null is not allowed', () => {
typeCheckNullFail(
PropTypes.objectOf(PropTypes.number).notNull
);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(
PropTypes.objectOf(PropTypes.number).isRequired,
Expand Down Expand Up @@ -830,6 +883,10 @@ describe('PropTypesDevelopmentStandalone', () => {
typeCheckPass(PropTypes.oneOf(['red', 'blue']), undefined);
});

it('should warn when null is not allowed', () => {
typeCheckNullFail(PropTypes.oneOf(['red', 'blue']).notNull);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(PropTypes.oneOf(['red', 'blue']).isRequired);
});
Expand Down Expand Up @@ -919,6 +976,12 @@ describe('PropTypesDevelopmentStandalone', () => {
);
});

it('should warn when null is not allowed', () => {
typeCheckNullFail(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]).notNull
);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
Expand Down Expand Up @@ -1017,6 +1080,12 @@ describe('PropTypesDevelopmentStandalone', () => {
);
});

it('should warn when null is not allowed', () => {
typeCheckNullFail(
PropTypes.shape({key: PropTypes.number}).notNull
);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(
PropTypes.shape({key: PropTypes.number}).isRequired,
Expand Down
9 changes: 6 additions & 3 deletions factoryWithTypeCheckers.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ module.exports = function(isValidElement, throwOnDirectAccess) {
var manualPropTypeCallCache = {};
var manualPropTypeWarningCount = 0;
}
function checkType(isRequired, props, propName, componentName, location, propFullName, secret) {
function checkType(isRequired, notNull, props, propName, componentName, location, propFullName, secret) {
componentName = componentName || ANONYMOUS;
propFullName = propFullName || propName;

Expand Down Expand Up @@ -192,15 +192,18 @@ module.exports = function(isValidElement, throwOnDirectAccess) {
return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.'));
}
return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.'));
} else if (notNull && props[propName] === null) {
return new PropTypeError('The ' + location + ' `' + propFullName + '` ' + ('in `' + componentName + '` must not be `null`.'));
}
return null;
} else {
return validate(props, propName, componentName, location, propFullName);
}
}

var chainedCheckType = checkType.bind(null, false);
chainedCheckType.isRequired = checkType.bind(null, true);
var chainedCheckType = checkType.bind(null, false, false);
chainedCheckType.isRequired = checkType.bind(null, true, true);
chainedCheckType.notNull = checkType.bind(null, false, true);

return chainedCheckType;
}
Expand Down