Skip to content

Commit

Permalink
Add option for strictNumbers. Resolves #1128.
Browse files Browse the repository at this point in the history
  • Loading branch information
Issac Gerges committed Jun 9, 2020
1 parent 0b14f60 commit edd8c92
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 9 deletions.
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);
});
};
}

0 comments on commit edd8c92

Please sign in to comment.