Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option for strictNumbers. Resolves #1128. #1219

Merged
merged 1 commit into from Jun 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Expand Up @@ -1156,6 +1156,7 @@ Defaults:
// strict mode options
strictDefaults: false,
strictKeywords: false,
strictNumbers: false,
// asynchronous validation options:
transpile: undefined, // requires ajv-async package
// advanced options:
Expand Down Expand Up @@ -1250,7 +1251,9 @@ Defaults:
- `false` (default) - unknown keywords are not reported
- `true` - if an unknown keyword is present, throw an error
- `"log"` - if an unknown keyword is present, log warning

- _strictNumbers_: validate numbers strictly, failing validation for NaN and Infinity. Option values:
- `false` (default) - NaN or Infinity will pass validation for numeric types
- `true` - NaN or Infinity will not pass validation for numeric types

##### Asynchronous validation options

Expand Down
1 change: 1 addition & 0 deletions lib/ajv.d.ts
Expand Up @@ -183,6 +183,7 @@ declare namespace ajv {
coerceTypes?: boolean | 'array';
strictDefaults?: boolean | 'log';
strictKeywords?: boolean | 'log';
strictNumbers?: boolean;
async?: boolean | string;
transpile?: string | ((code: string) => string);
meta?: boolean | object;
Expand Down
13 changes: 8 additions & 5 deletions lib/compile/util.js
Expand Up @@ -36,7 +36,7 @@ function copy(o, to) {
}


function checkDataType(dataType, data, negate) {
function checkDataType(dataType, data, strictNumbers, negate) {
var EQUAL = negate ? ' !== ' : ' === '
, AND = negate ? ' || ' : ' && '
, OK = negate ? '!' : ''
Expand All @@ -49,15 +49,18 @@ function checkDataType(dataType, data, negate) {
NOT + 'Array.isArray(' + data + '))';
case 'integer': return '(typeof ' + data + EQUAL + '"number"' + AND +
NOT + '(' + data + ' % 1)' +
AND + data + EQUAL + data + ')';
AND + data + EQUAL + data +
(strictNumbers ? (AND + OK + 'isFinite(' + data + ')') : '') + ')';
case 'number': return '(typeof ' + data + EQUAL + '"' + dataType + '"' +
(strictNumbers ? (AND + OK + 'isFinite(' + data + ')') : '') + ')';
default: return 'typeof ' + data + EQUAL + '"' + dataType + '"';
}
}


function checkDataTypes(dataTypes, data) {
function checkDataTypes(dataTypes, data, strictNumbers) {
switch (dataTypes.length) {
case 1: return checkDataType(dataTypes[0], data, true);
case 1: return checkDataType(dataTypes[0], data, strictNumbers, true);
default:
var code = '';
var types = toHash(dataTypes);
Expand All @@ -70,7 +73,7 @@ function checkDataTypes(dataTypes, data) {
}
if (types.number) delete types.integer;
for (var t in types)
code += (code ? ' && ' : '' ) + checkDataType(t, data, true);
code += (code ? ' && ' : '' ) + checkDataType(t, data, strictNumbers, true);

return code;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/dot/uniqueItems.jst
Expand Up @@ -38,7 +38,7 @@
for (;i--;) {
var item = {{=$data}}[i];
{{ var $method = 'checkDataType' + ($typeIsArray ? 's' : ''); }}
if ({{= it.util[$method]($itemType, 'item', true) }}) continue;
if ({{= it.util[$method]($itemType, 'item', it.opts.strictNumbers, true) }}) continue;
{{? $typeIsArray}}
if (typeof item == 'string') item = '"' + item;
{{?}}
Expand Down
4 changes: 2 additions & 2 deletions lib/dot/validate.jst
Expand Up @@ -140,7 +140,7 @@
, $method = $typeIsArray ? 'checkDataTypes' : 'checkDataType';
}}

if ({{= it.util[$method]($typeSchema, $data, true) }}) {
if ({{= it.util[$method]($typeSchema, $data, it.opts.strictNumbers, true) }}) {
#}}

{{? it.schema.$ref && $refKeywords }}
Expand Down Expand Up @@ -192,7 +192,7 @@
{{~ it.RULES:$rulesGroup }}
{{? $shouldUseGroup($rulesGroup) }}
{{? $rulesGroup.type }}
if ({{= it.util.checkDataType($rulesGroup.type, $data) }}) {
if ({{= it.util.checkDataType($rulesGroup.type, $data, it.opts.strictNumbers) }}) {
{{?}}
{{? it.opts.useDefaults }}
{{? $rulesGroup.type == 'object' && it.schema.properties }}
Expand Down
55 changes: 55 additions & 0 deletions spec/options/strictNumbers.spec.js
@@ -0,0 +1,55 @@
'use strict';

var Ajv = require('../ajv');

describe('structNumbers option', function() {
var ajv;
describe('strictNumbers default', testWithoutStrictNumbers(new Ajv()));
describe('strictNumbers = false', testWithoutStrictNumbers(new Ajv({strictNumbers: false})));
describe('strictNumbers = true', function() {
beforeEach(function () {
ajv = new Ajv({strictNumbers: true});
});

it('should fail validation for NaN/Infinity as type number', function() {
var validate = ajv.compile({type: 'number'});
validate("1.1").should.equal(false);
validate(1.1).should.equal(true);
validate(1).should.equal(true);
validate(NaN).should.equal(false);
validate(Infinity).should.equal(false);
});

it('should fail validation for NaN as type integer', function() {
var validate = ajv.compile({type: 'integer'});
validate("1.1").should.equal(false);
validate(1.1).should.equal(false);
validate(1).should.equal(true);
validate(NaN).should.equal(false);
validate(Infinity).should.equal(false);
});
});
});


function testWithoutStrictNumbers(_ajv) {
return function () {
it('should NOT fail validation for NaN/Infinity as type number', function() {
var validate = _ajv.compile({type: 'number'});
validate("1.1").should.equal(false);
validate(1.1).should.equal(true);
validate(1).should.equal(true);
validate(NaN).should.equal(true);
validate(Infinity).should.equal(true);
});

it('should NOT fail validation for NaN/Infinity as type integer', function() {
var validate = _ajv.compile({type: 'integer'});
validate("1.1").should.equal(false);
validate(1.1).should.equal(false);
validate(1).should.equal(true);
validate(NaN).should.equal(false);
validate(Infinity).should.equal(true);
});
};
}