Skip to content

Commit

Permalink
Add option for strictNumbers. Resolves ajv-validator#1128.
Browse files Browse the repository at this point in the history
  • Loading branch information
Issac Gerges committed May 27, 2020
1 parent 0b14f60 commit 3f25187
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 8 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 a number type
- `true` - NaN or Infinity will not pass validation for a number type

##### 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
9 changes: 5 additions & 4 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 @@ -50,14 +50,15 @@ function checkDataType(dataType, data, negate) {
case 'integer': return '(typeof ' + data + EQUAL + '"number"' + AND +
NOT + '(' + data + ' % 1)' +
AND + data + EQUAL + 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 +71,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
47 changes: 47 additions & 0 deletions spec/options/strictNumbers.spec.js
@@ -0,0 +1,47 @@
'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).should.equal(true);
validate(NaN).should.equal(false);
validate(Infinity).should.equal(false);
});

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


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).should.equal(true);
validate(NaN).should.equal(true);
validate(Infinity).should.equal(true);
});

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

0 comments on commit 3f25187

Please sign in to comment.