diff --git a/package-lock.json b/package-lock.json index 96549cae89..4077f8fb96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5423,7 +5423,7 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, @@ -5859,7 +5859,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { diff --git a/src/validation/ValidationTypeOptions.ts b/src/validation/ValidationTypeOptions.ts index fe91f7a91d..89408932c2 100644 --- a/src/validation/ValidationTypeOptions.ts +++ b/src/validation/ValidationTypeOptions.ts @@ -1,7 +1,8 @@ /** - * Options to be passed to IsURL decorator. + * Options to be passed to IsNumber decorator. */ export interface IsNumberOptions { allowNaN?: boolean; allowInfinity?: boolean; -} \ No newline at end of file + maxDecimalPlaces?: number; +} diff --git a/src/validation/ValidationTypes.ts b/src/validation/ValidationTypes.ts index 085fc22dbd..1b206dd7aa 100644 --- a/src/validation/ValidationTypes.ts +++ b/src/validation/ValidationTypes.ts @@ -136,7 +136,7 @@ export class ValidationTypes { case this.IS_DATE: return eachPrefix + "$property must be a Date instance"; case this.IS_NUMBER: - return eachPrefix + "$property must be a number"; + return eachPrefix + "$property must be a number conforming to the specified constraints"; case this.IS_INT: return eachPrefix + "$property must be an integer number"; case this.IS_STRING: diff --git a/src/validation/Validator.ts b/src/validation/Validator.ts index 366a3b4e3e..89ae4bc97d 100644 --- a/src/validation/Validator.ts +++ b/src/validation/Validator.ts @@ -377,6 +377,16 @@ export class Validator { return options.allowNaN; } + if (options.maxDecimalPlaces) { + let decimalPlaces = 0; + if ((value % 1) !== 0) { + decimalPlaces = value.toString().split(".")[1].length; + } + if (decimalPlaces > options.maxDecimalPlaces) { + return false; + } + } + return Number.isFinite(value); } diff --git a/test/functional/validation-functions-and-decorators.spec.ts b/test/functional/validation-functions-and-decorators.spec.ts index 0284a18eed..bd0649f58c 100644 --- a/test/functional/validation-functions-and-decorators.spec.ts +++ b/test/functional/validation-functions-and-decorators.spec.ts @@ -479,6 +479,11 @@ describe("IsNumber", function() { someProperty: number; } + class MaxDecimalPlacesTest { + @IsNumber({ maxDecimalPlaces: 3 }) + someProperty: number; + } + it("should fail if NaN passed without allowing NaN values", function (done) { checkInvalidValues(new MyClass(), [NaN], done); }); @@ -513,10 +518,18 @@ describe("IsNumber", function() { it("should return error object with proper data", function(done) { const validationType = "isNumber"; - const message = "someProperty must be a number"; + const message = "someProperty must be a number conforming to the specified constraints"; checkReturnedError(new MyClass(), invalidValues, validationType, message, done); }); + it("should pass if number of decimal places within maxDecimalPlaces", function(done) { + checkValidValues(new MaxDecimalPlacesTest(), [1.123], done); + }); + + it("should fail if number of decimal places exceeds maxDecimalPlaces", function(done) { + checkInvalidValues(new MaxDecimalPlacesTest(), [1.1234], done); + }); + }); describe("IsInt", function() {