Repository: icebob/fastest-validator
Pull request #317 (2023-02-18 01:20:59)

consider null as a value
This PR contains breaking changes and should be reviewed with care.

All fields are considered `nullable` by default.

It allows a field to be:
 - `required` and `null`.
 - `optional` but **not** `null`.

fix https://github.com/icebob/fastest-validator/issues/296


Patch:
diff --git a/README.md b/README.md
index f83607b..493d1ef 100644
--- a/README.md
+++ b/README.md
@@ -191,6 +191,32 @@ object2.about // is null
 
 check({ about: "Custom" }) // Valid
 ```
+### Considering `null` as a value
+In specific case, you may want to consider `null` as a valid input even for a `required` field.  
+
+It's useful in cases you want a field to be:
+ - `required` and `null` without specifying `nullable: true` in its definition.
+ - `required` and not `null` by specifying `nullable: false` in its definition.
+ - `optional` **but specifically not** `null`.  
+
+To be able to achieve this you'll have to set the `considerNullAsAValue` validator option to `true`. 
+```js
+const v = new Validator({considerNullAsAValue: true});
+
+const schema = {foo: {type: "number"}, bar: {type: "number", optional: true, nullable: false}, baz: {type: "number", nullable: false}};
+const check = v.compile(schema);
+
+const object1 = {foo: null, baz: 1};
+check(object1); // valid (foo is required and can be null)
+
+const object2 = {foo: 3, bar: null, baz: 1};
+check(object2); // not valid (bar is optional but can't be null)
+
+const object3 = {foo: 3, baz: null};
+check(object3); // not valid (baz is required but can't be null)
+
+```
+With this option set all fields will be considered _nullable_ by default.
 
 # Strict validation
 Object properties which are not specified on the schema are ignored by default. If you set the `$$strict` option to `true` any additional properties will result in an `strictObject` error.
diff --git a/index.d.ts b/index.d.ts
index d51daa2..05bf241 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -907,6 +907,11 @@ export interface ValidatorConstructorOptions {
 	 */
 	useNewCustomCheckerFunction?: boolean;
 
+	/**
+	 * consider null as a value?
+	 */
+	considerNullAsAValue?: boolean;
+
 	/**
 	 * Default settings for rules
 	 */
diff --git a/lib/validator.js b/lib/validator.js
index d39b697..034f925 100644
--- a/lib/validator.js
+++ b/lib/validator.js
@@ -113,15 +113,26 @@ class Validator {
 	 */
 	wrapRequiredCheckSourceCode(rule, innerSrc, context, resVar) {
 		const src = [];
+		const {considerNullAsAValue = false} = this.opts;
 		let handleNoValue;
 
 		let skipUndefinedValue = rule.schema.optional === true || rule.schema.type === "forbidden";
-		let skipNullValue = rule.schema.optional === true || rule.schema.nullable === true || rule.schema.type === "forbidden";
+		let skipNullValue = considerNullAsAValue ?
+			rule.schema.nullable !== false || rule.schema.type === "forbidden" :
+			rule.schema.optional === true || rule.schema.nullable === true || rule.schema.type === "forbidden";
 
-		if (rule.schema.default != null) {
+		const ruleHasDefault = considerNullAsAValue ? 
+			rule.schema.default != undefined && rule.schema.default != null : 
+			rule.schema.default != undefined;
+
+		if (ruleHasDefault) {
 			// We should set default-value when value is undefined or null, not skip! (Except when null is allowed)
 			skipUndefinedValue = false;
-			if (rule.schema.nullable !== true) skipNullValue = false;
+			if (considerNullAsAValue) {
+				if (rule.schema.nullable === false) skipNullValue = false;
+			} else {
+				if (rule.schema.nullable !== true) skipNullValue = false;
+			}
 
 			let defaultValue;
 			if (typeof rule.schema.default === "function") {
@@ -504,11 +515,12 @@ class Validator {
 				schema.optional = true;
 
 			// Check 'nullable' flag
-			const isNullable = schema.rules
+			const nullCheck = this.opts.considerNullAsAValue ? false : true;
+			const setNullable = schema.rules
 				.map(s => this.getRuleFromSchema(s))
-				.every(rule => rule.schema.nullable === true);
-			if (isNullable)
-				schema.nullable = true;
+				.every(rule => rule.schema.nullable === nullCheck);
+			if (setNullable)
+				schema.nullable = nullCheck;
 		}
 
 		if (schema.$$type) {


Test patch:
diff --git a/test/integration.spec.js b/test/integration.spec.js
index b10f7b4..d7e310e 100644
--- a/test/integration.spec.js
+++ b/test/integration.spec.js
@@ -1109,75 +1109,218 @@ describe("Test optional option", () => {
 });
 
 describe("Test nullable option", () => {
-	const v = new Validator();
+	describe("old case", () => {
+		const v = new Validator();
 
-	it("should throw error if value is undefined", () => {
-		const schema = { foo: { type: "number", nullable: true } };
-		const check = v.compile(schema);
+		it("should throw error if value is undefined", () => {
+			const schema = { foo: { type: "number", nullable: true } };
+			const check = v.compile(schema);
 
-		expect(check(check)).toBeInstanceOf(Array);
-		expect(check({ foo: undefined })).toBeInstanceOf(Array);
-	});
+			expect(check(check)).toBeInstanceOf(Array);
+			expect(check({ foo: undefined })).toBeInstanceOf(Array);
+		});
 
-	it("should not throw error if value is null", () => {
-		const schema = { foo: { type: "number", nullable: true } };
-		const check = v.compile(schema);
+		it("should not throw error if value is null", () => {
+			const schema = { foo: { type: "number", nullable: true } };
+			const check = v.compile(schema);
 
-		const o = { foo: null };
-		expect(check(o)).toBe(true);
-		expect(o.foo).toBe(null);
-	});
+			const o = { foo: null };
+			expect(check(o)).toBe(true);
+			expect(o.foo).toBe(null);
+		});
 
-	it("should not throw error if value exist", () => {
-		const schema = { foo: { type: "number", nullable: true } };
-		const check = v.compile(schema);
-		expect(check({ foo: 2 })).toBe(true);
-	});
+		it("should not throw error if value exist", () => {
+			const schema = { foo: { type: "number", nullable: true } };
+			const check = v.compile(schema);
+			expect(check({ foo: 2 })).toBe(true);
+		});
 
-	it("should set default value if there is a default", () => {
-		const schema = { foo: { type: "number", nullable: true, default: 5 } };
-		const check = v.compile(schema);
+		it("should set default value if there is a default", () => {
+			const schema = { foo: { type: "number", nullable: true, default: 5 } };
+			const check = v.compile(schema);
 
-		const o1 = { foo: 2 };
-		expect(check(o1)).toBe(true);
-		expect(o1.foo).toBe(2);
+			const o1 = { foo: 2 };
+			expect(check(o1)).toBe(true);
+			expect(o1.foo).toBe(2);
 
-		const o2 = {};
-		expect(check(o2)).toBe(true);
-		expect(o2.foo).toBe(5);
-	});
+			const o2 = {};
+			expect(check(o2)).toBe(true);
+			expect(o2.foo).toBe(5);
+		});
 
-	it("should not set default value if current value is null", () => {
-		const schema = { foo: { type: "number", nullable: true, default: 5 } };
-		const check = v.compile(schema);
+		it("should not set default value if current value is null", () => {
+			const schema = { foo: { type: "number", nullable: true, default: 5 } };
+			const check = v.compile(schema);
 
-		const o = { foo: null };
-		expect(check(o)).toBe(true);
-		expect(o.foo).toBe(null);
-	});
+			const o = { foo: null };
+			expect(check(o)).toBe(true);
+			expect(o.foo).toBe(null);
+		});
 
-	it("should work with optional", () => {
-		const schema = { foo: { type: "number", nullable: true, optional: true } };
-		const check = v.compile(schema);
+		it("should work with optional", () => {
+			const schema = { foo: { type: "number", nullable: true, optional: true } };
+			const check = v.compile(schema);
 
-		expect(check({ foo: 3 })).toBe(true);
-		expect(check({ foo: null })).toBe(true);
-		expect(check({})).toBe(true);
+			expect(check({ foo: 3 })).toBe(true);
+			expect(check({ foo: null })).toBe(true);
+			expect(check({})).toBe(true);
+		});
+
+		it("should work with optional and default", () => {
+			const schema = { foo: { type: "number", nullable: true, optional: true, default: 5 } };
+			const check = v.compile(schema);
+
+			expect(check({ foo: 3 })).toBe(true);
+
+			const o1 = { foo: null };
+			expect(check(o1)).toBe(true);
+			expect(o1.foo).toBe(null);
+
+			const o2 = {};
+			expect(check(o2)).toBe(true);
+			expect(o2.foo).toBe(5);
+		});
+
+		it("should accept null value when optional", () => {
+			const schema = { foo: { type: "number", nullable: false, optional: true } };
+			const check = v.compile(schema);
+
+			expect(check({ foo: 3 })).toBe(true);
+			expect(check({ foo: undefined })).toBe(true);
+			expect(check({})).toBe(true);
+			expect(check({ foo: null })).toBe(true);
+		});
+
+		it("should accept null as value when required", () => {
+			const schema = {foo: {type: "number", nullable: true, optional: false}};
+			const check = v.compile(schema);
+
+			expect(check({ foo: 3 })).toBe(true);
+			expect(check({ foo: undefined })).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+			expect(check({})).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+			expect(check({ foo: null })).toBe(true);
+		});
+
+		it("should not accept null as value when required and not explicitly not nullable", () => {
+			const schema = {foo: {type: "number", optional: false}};
+			const check = v.compile(schema);
+
+			expect(check({ foo: 3 })).toBe(true);
+			expect(check({ foo: undefined })).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+			expect(check({})).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+			expect(check({ foo: null })).toEqual([{"actual": null, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+		});
 	});
 
-	it("should work with optional and default", () => {
-		const schema = { foo: { type: "number", nullable: true, optional: true, default: 5 } };
-		const check = v.compile(schema);
+	describe("new case (with considerNullAsAValue flag set to true)", () => {
+		const v = new Validator({considerNullAsAValue: true});
 
-		expect(check({ foo: 3 })).toBe(true);
+		it("should throw error if value is undefined", () => {
+			const schema = { foo: { type: "number" } };
+			const check = v.compile(schema);
 
-		const o1 = { foo: null };
-		expect(check(o1)).toBe(true);
-		expect(o1.foo).toBe(null);
+			expect(check(check)).toBeInstanceOf(Array);
+			expect(check({ foo: undefined })).toBeInstanceOf(Array);
+		});
 
-		const o2 = {};
-		expect(check(o2)).toBe(true);
-		expect(o2.foo).toBe(5);
+		it("should not throw error if value is null", () => {
+			const schema = { foo: { type: "number" } };
+			const check = v.compile(schema);
+
+			const o = { foo: null };
+			expect(check(o)).toBe(true);
+			expect(o.foo).toBe(null);
+		});
+
+		it("should not throw error if value exist", () => {
+			const schema = { foo: { type: "number" } };
+			const check = v.compile(schema);
+			expect(check({ foo: 2 })).toBe(true);
+		});
+
+		it("should set default value if there is a default", () => {
+			const schema = { foo: { type: "number", default: 5 } };
+			const check = v.compile(schema);
+
+			const o1 = { foo: 2 };
+			expect(check(o1)).toBe(true);
+			expect(o1.foo).toBe(2);
+
+			const o2 = {};
+			expect(check(o2)).toBe(true);
+			expect(o2.foo).toBe(5);
+		});
+
+		it("should not set default value if current value is null", () => {
+			const schema = { foo: { type: "number", default: 5 } };
+			const check = v.compile(schema);
+
+			const o = { foo: null };
+			expect(check(o)).toBe(true);
+			expect(o.foo).toBe(null);
+		});
+
+		it("should set default value if current value is null but can't be", () => {
+			const schema = { foo: { type: "number", default: 5, nullable: false } };
+			const check = v.compile(schema);
+
+			const o = { foo: null };
+			expect(check(o)).toBe(true);
+			expect(o.foo).toBe(5);
+		});
+
+		it("should set default value if current value is null but optional", () => {
+			const schema = { foo: { type: "number", default: 5, nullable: false, optional: true } };
+			const check = v.compile(schema);
+
+			const o = { foo: null };
+			expect(check(o)).toBe(true);
+			expect(o.foo).toBe(5);
+		});
+
+		it("should work with optional", () => {
+			const schema = { foo: { type: "number", optional: true } };
+			const check = v.compile(schema);
+
+			expect(check({ foo: 3 })).toBe(true);
+			expect(check({ foo: null })).toBe(true);
+			expect(check({})).toBe(true);
+		});
+
+		it("should work with optional and default", () => {
+			const schema = { foo: { type: "number", optional: true, default: 5 } };
+			const check = v.compile(schema);
+
+			expect(check({ foo: 3 })).toBe(true);
+
+			const o1 = { foo: null };
+			expect(check(o1)).toBe(true);
+			expect(o1.foo).toBe(null);
+
+			const o2 = {};
+			expect(check(o2)).toBe(true);
+			expect(o2.foo).toBe(5);
+		});
+
+		it("should not accept null value even if optional", () => {
+			const schema = { foo: { type: "number", nullable: false, optional: true } };
+			const check = v.compile(schema);
+
+			expect(check({ foo: 3 })).toBe(true);
+			expect(check({ foo: undefined })).toBe(true);
+			expect(check({})).toBe(true);
+			expect(check({ foo: null })).toEqual([{"actual": null, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+		});
+
+		it("should not accept null as value", () => {
+			const schema = {foo: {type: "number", nullable: false}};
+			const check = v.compile(schema);
+
+			expect(check({ foo: 3 })).toBe(true);
+			expect(check({ foo: undefined })).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+			expect(check({})).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+			expect(check({ foo: null })).toEqual([{"actual": null, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+		});
 	});
 });
 
diff --git a/test/rules/any.spec.js b/test/rules/any.spec.js
index 4d86a7f..b8124e6 100644
--- a/test/rules/any.spec.js
+++ b/test/rules/any.spec.js
@@ -1,40 +1,75 @@
 "use strict";
 
 const Validator = require("../../lib/validator");
-const v = new Validator();
 const anyRule = require("../../lib/rules/any");
 
 describe("Test rule: any", () => {
 
-	it("should give back true anyway", () => {
-		const check = v.compile({ $$root: true, type: "any" });
-
-		expect(check(null)).toEqual([{ type: "required", actual: null, message: "The '' field is required." }]);
-		expect(check(undefined)).toEqual([{ type: "required", actual: undefined, message: "The '' field is required." }]);
-		expect(check(0)).toEqual(true);
-		expect(check(1)).toEqual(true);
-		expect(check("")).toEqual(true);
-		expect(check("true")).toEqual(true);
-		expect(check("false")).toEqual(true);
-		expect(check([])).toEqual(true);
-		expect(check({})).toEqual(true);
+	it("should have source code", () => {
+		expect(anyRule().source).toBeTruthy();
 	});
 
-	it("should give back true anyway", () => {
-		const check = v.compile({ $$root: true, type: "any", optional: true });
-
-		expect(check(null)).toEqual(true);
-		expect(check(undefined)).toEqual(true);
-		expect(check(0)).toEqual(true);
-		expect(check(1)).toEqual(true);
-		expect(check("")).toEqual(true);
-		expect(check("true")).toEqual(true);
-		expect(check("false")).toEqual(true);
-		expect(check([])).toEqual(true);
-		expect(check({})).toEqual(true);
+	describe("old case (without considerNullAsAValue flag)", () => {
+		const v = new Validator();
+
+		it("should give back true anyway", () => {
+			const check = v.compile({ $$root: true, type: "any" });
+
+			expect(check(null)).toEqual([{ type: "required", actual: null, message: "The '' field is required." }]);
+			expect(check(undefined)).toEqual([{ type: "required", actual: undefined, message: "The '' field is required." }]);
+			expect(check(0)).toEqual(true);
+			expect(check(1)).toEqual(true);
+			expect(check("")).toEqual(true);
+			expect(check("true")).toEqual(true);
+			expect(check("false")).toEqual(true);
+			expect(check([])).toEqual(true);
+			expect(check({})).toEqual(true);
+		});
+
+		it("should give back true anyway as optional", () => {
+			const check = v.compile({ $$root: true, type: "any", optional: true });
+
+			expect(check(null)).toEqual(true);
+			expect(check(undefined)).toEqual(true);
+			expect(check(0)).toEqual(true);
+			expect(check(1)).toEqual(true);
+			expect(check("")).toEqual(true);
+			expect(check("true")).toEqual(true);
+			expect(check("false")).toEqual(true);
+			expect(check([])).toEqual(true);
+			expect(check({})).toEqual(true);
+		});
 	});
 
-	it("should have source code", () => {
-		expect(anyRule().source).toBeTruthy();
+	describe("new case (with considerNullAsAValue flag set to true)", () => {
+		const v = new Validator({considerNullAsAValue: true});
+	
+		it("should give back true anyway", () => {
+			const check = v.compile({ $$root: true, type: "any" });
+	
+			expect(check(null)).toEqual(true);
+			expect(check(undefined)).toEqual([{ type: "required", actual: undefined, message: "The '' field is required." }]);
+			expect(check(0)).toEqual(true);
+			expect(check(1)).toEqual(true);
+			expect(check("")).toEqual(true);
+			expect(check("true")).toEqual(true);
+			expect(check("false")).toEqual(true);
+			expect(check([])).toEqual(true);
+			expect(check({})).toEqual(true);
+		});
+	
+		it("should give back true anyway as optional", () => {
+			const check = v.compile({ $$root: true, type: "any", optional: true });
+	
+			expect(check(null)).toEqual(true);
+			expect(check(undefined)).toEqual(true);
+			expect(check(0)).toEqual(true);
+			expect(check(1)).toEqual(true);
+			expect(check("")).toEqual(true);
+			expect(check("true")).toEqual(true);
+			expect(check("false")).toEqual(true);
+			expect(check([])).toEqual(true);
+			expect(check({})).toEqual(true);
+		});
 	});
-});
+});
\ No newline at end of file
diff --git a/test/rules/array.spec.js b/test/rules/array.spec.js
index 93be05d..38c33b9 100644
--- a/test/rules/array.spec.js
+++ b/test/rules/array.spec.js
@@ -230,6 +230,25 @@ describe("Test rule: array", () => {
 				expect(check(value2)).toEqual([{ type: "required", field: "data", actual: undefined, message: "The 'data' field is required." }]);
 				expect(value2.data).toEqual(undefined);
 			});
+
+			it ("should not convert into array if undefined (new case)", () => {
+				const v = new Validator({
+					useNewCustomCheckerFunction: true,
+					considerNullAsAValue: true,
+					messages: {
+						evenNumber: "The '' field must be an even number!"
+					}
+				});
+				const check = v.compile({ data: { type: "array", items: "string", convert: true } });
+				// Null check
+				const value = { data: null };
+				expect(check(value)).toEqual(true);
+				expect(value.data).toEqual(null);
+				// Undefined check
+				const value2 = { data: undefined };
+				expect(check(value2)).toEqual([{ type: "required", field: "data", actual: undefined, message: "The 'data' field is required." }]);
+				expect(value2.data).toEqual(undefined);
+			});
 		});
 	});
 });
diff --git a/test/typescript/integration.spec.ts b/test/typescript/integration.spec.ts
index 90d1e97..e4a1cdb 100644
--- a/test/typescript/integration.spec.ts
+++ b/test/typescript/integration.spec.ts
@@ -1128,75 +1128,218 @@ describe('TypeScript Definitions', () => {
 	});
 
 	describe("Test nullable option", () => {
-		const v = new Validator();
+		describe("old case", () => {
+			const v = new Validator();
 
-		it("should throw error if value is undefined", () => {
-			const schema = { foo: { type: "number", nullable: true } };
-			const check = v.compile(schema);
+			it("should throw error if value is undefined", () => {
+				const schema = { foo: { type: "number", nullable: true } };
+				const check = v.compile(schema);
 
-			expect(check(check)).toBeInstanceOf(Array);
-			expect(check({ foo: undefined })).toBeInstanceOf(Array);
-		});
+				expect(check(check)).toBeInstanceOf(Array);
+				expect(check({ foo: undefined })).toBeInstanceOf(Array);
+			});
 
-		it("should not throw error if value is null", () => {
-			const schema = { foo: { type: "number", nullable: true } };
-			const check = v.compile(schema);
+			it("should not throw error if value is null", () => {
+				const schema = { foo: { type: "number", nullable: true } };
+				const check = v.compile(schema);
 
-			const o = { foo: null };
-			expect(check(o)).toBe(true);
-			expect(o.foo).toBe(null);
-		});
+				const o = { foo: null };
+				expect(check(o)).toBe(true);
+				expect(o.foo).toBe(null);
+			});
 
-		it("should not throw error if value exist", () => {
-			const schema = { foo: { type: "number", nullable: true } };
-			const check = v.compile(schema);
-			expect(check({ foo: 2 })).toBe(true);
-		});
+			it("should not throw error if value exist", () => {
+				const schema = { foo: { type: "number", nullable: true } };
+				const check = v.compile(schema);
+				expect(check({ foo: 2 })).toBe(true);
+			});
 
-		it("should set default value if there is a default", () => {
-			const schema = { foo: { type: "number", nullable: true, default: 5 } };
-			const check = v.compile(schema);
+			it("should set default value if there is a default", () => {
+				const schema = { foo: { type: "number", nullable: true, default: 5 } };
+				const check = v.compile(schema);
 
-			const o1 = { foo: 2 };
-			expect(check(o1)).toBe(true);
-			expect(o1.foo).toBe(2);
+				const o1 = { foo: 2 };
+				expect(check(o1)).toBe(true);
+				expect(o1.foo).toBe(2);
 
-			const o2: { foo?: number } = {};
-			expect(check(o2)).toBe(true);
-			expect(o2.foo).toBe(5);
-		});
+				const o2: { foo?: number } = {};
+				expect(check(o2)).toBe(true);
+				expect(o2.foo).toBe(5);
+			});
 
-		it("should not set default value if current value is null", () => {
-			const schema = { foo: { type: "number", nullable: true, default: 5 } };
-			const check = v.compile(schema);
+			it("should not set default value if current value is null", () => {
+				const schema = { foo: { type: "number", nullable: true, default: 5 } };
+				const check = v.compile(schema);
 
-			const o = { foo: null };
-			expect(check(o)).toBe(true);
-			expect(o.foo).toBe(null);
-		});
+				const o = { foo: null };
+				expect(check(o)).toBe(true);
+				expect(o.foo).toBe(null);
+			});
 
-		it("should work with optional", () => {
-			const schema = { foo: { type: "number", nullable: true, optional: true } };
-			const check = v.compile(schema);
+			it("should work with optional", () => {
+				const schema = { foo: { type: "number", nullable: true, optional: true } };
+				const check = v.compile(schema);
 
-			expect(check({ foo: 3 })).toBe(true);
-			expect(check({ foo: null })).toBe(true);
-			expect(check({})).toBe(true);
+				expect(check({ foo: 3 })).toBe(true);
+				expect(check({ foo: null })).toBe(true);
+				expect(check({})).toBe(true);
+			});
+
+			it("should work with optional and default", () => {
+				const schema = { foo: { type: "number", nullable: true, optional: true, default: 5 } };
+				const check = v.compile(schema);
+
+				expect(check({ foo: 3 })).toBe(true);
+
+				const o1 = { foo: null };
+				expect(check(o1)).toBe(true);
+				expect(o1.foo).toBe(null);
+
+				const o2: { foo?: number } = {};
+				expect(check(o2)).toBe(true);
+				expect(o2.foo).toBe(5);
+			});
+
+			it("should accept null value when optional", () => {
+				const schema = { foo: { type: "number", nullable: false, optional: true } };
+				const check = v.compile(schema);
+
+				expect(check({ foo: 3 })).toBe(true);
+				expect(check({ foo: undefined })).toBe(true);
+				expect(check({})).toBe(true);
+				expect(check({ foo: null })).toBe(true);
+			});
+
+			it("should accept null as value when required", () => {
+				const schema = {foo: {type: "number", nullable: true, optional: false}};
+				const check = v.compile(schema);
+
+				expect(check({ foo: 3 })).toBe(true);
+				expect(check({ foo: undefined })).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+				expect(check({})).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+				expect(check({ foo: null })).toBe(true);
+			});
+
+			it("should not accept null as value when required and not explicitly not nullable", () => {
+				const schema = {foo: {type: "number", optional: false}};
+				const check = v.compile(schema);
+	
+				expect(check({ foo: 3 })).toBe(true);
+				expect(check({ foo: undefined })).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+				expect(check({})).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+				expect(check({ foo: null })).toEqual([{"actual": null, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+			});
 		});
 
-		it("should work with optional and default", () => {
-			const schema = { foo: { type: "number", nullable: true, optional: true, default: 5 } };
-			const check = v.compile(schema);
+		describe("new case (with considerNullAsAValue flag set to true)", () => {
+			const v = new Validator({considerNullAsAValue: true});
 
-			expect(check({ foo: 3 })).toBe(true);
+			it("should throw error if value is undefined", () => {
+				const schema = { foo: { type: "number" } };
+				const check = v.compile(schema);
 
-			const o1 = { foo: null };
-			expect(check(o1)).toBe(true);
-			expect(o1.foo).toBe(null);
+				expect(check(check)).toBeInstanceOf(Array);
+				expect(check({ foo: undefined })).toBeInstanceOf(Array);
+			});
 
-			const o2: { foo?: number } = {};
-			expect(check(o2)).toBe(true);
-			expect(o2.foo).toBe(5);
+			it("should not throw error if value is null", () => {
+				const schema = { foo: { type: "number" } };
+				const check = v.compile(schema);
+
+				const o = { foo: null };
+				expect(check(o)).toBe(true);
+				expect(o.foo).toBe(null);
+			});
+
+			it("should not throw error if value exist", () => {
+				const schema = { foo: { type: "number" } };
+				const check = v.compile(schema);
+				expect(check({ foo: 2 })).toBe(true);
+			});
+
+			it("should set default value if there is a default", () => {
+				const schema = { foo: { type: "number", default: 5 } };
+				const check = v.compile(schema);
+
+				const o1 = { foo: 2 };
+				expect(check(o1)).toBe(true);
+				expect(o1.foo).toBe(2);
+
+				const o2: { foo?: number } = {};
+				expect(check(o2)).toBe(true);
+				expect(o2.foo).toBe(5);
+			});
+
+			it("should not set default value if current value is null", () => {
+				const schema = { foo: { type: "number", default: 5 } };
+				const check = v.compile(schema);
+
+				const o = { foo: null };
+				expect(check(o)).toBe(true);
+				expect(o.foo).toBe(null);
+			});
+
+			it("should set default value if current value is null but can't be", () => {
+				const schema = { foo: { type: "number", default: 5, nullable: false } };
+				const check = v.compile(schema);
+	
+				const o = { foo: null };
+				expect(check(o)).toBe(true);
+				expect(o.foo).toBe(5);
+			});
+
+			it("should set default value if current value is null but optional", () => {
+				const schema = { foo: { type: "number", default: 5, nullable: false, optional: true } };
+				const check = v.compile(schema);
+	
+				const o = { foo: null };
+				expect(check(o)).toBe(true);
+				expect(o.foo).toBe(5);
+			});
+
+			it("should work with optional", () => {
+				const schema = { foo: { type: "number", optional: true } };
+				const check = v.compile(schema);
+
+				expect(check({ foo: 3 })).toBe(true);
+				expect(check({ foo: null })).toBe(true);
+				expect(check({})).toBe(true);
+			});
+
+			it("should work with optional and default", () => {
+				const schema = { foo: { type: "number", optional: true, default: 5 } };
+				const check = v.compile(schema);
+
+				expect(check({ foo: 3 })).toBe(true);
+
+				const o1 = { foo: null };
+				expect(check(o1)).toBe(true);
+				expect(o1.foo).toBe(null);
+
+				const o2: { foo?: number } = {};
+				expect(check(o2)).toBe(true);
+				expect(o2.foo).toBe(5);
+			});
+
+			it("should not accept null value even if optional", () => {
+				const schema = { foo: { type: "number", nullable: false, optional: true } };
+				const check = v.compile(schema);
+
+				expect(check({ foo: 3 })).toBe(true);
+				expect(check({ foo: undefined })).toBe(true);
+				expect(check({})).toBe(true);
+				expect(check({ foo: null })).toEqual([{"actual": null, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+			});
+
+			it("should not accept null as value", () => {
+				const schema = {foo: {type: "number", nullable: false}};
+				const check = v.compile(schema);
+	
+				expect(check({ foo: 3 })).toBe(true);
+				expect(check({ foo: undefined })).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+				expect(check({})).toEqual([{"actual": undefined, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+				expect(check({ foo: null })).toEqual([{"actual": null, "field": "foo", "message": "The 'foo' field is required.", "type": "required"}]);
+			});
 		});
 	});
 })
diff --git a/test/typescript/rules/any.spec.ts b/test/typescript/rules/any.spec.ts
index 7710307..86a2cbf 100644
--- a/test/typescript/rules/any.spec.ts
+++ b/test/typescript/rules/any.spec.ts
@@ -3,34 +3,69 @@ import Validator from "../../../";
 const v = new Validator();
 
 describe('TypeScript Definitions', () => {
-	describe('Test rule: any', () => {
-
-		it('should give back true anyway', () => {
-			const check = v.compile({ $$root: true, type: 'any' });
-
-			expect(check(null)).toEqual([{ type: 'required', actual: null, message: 'The \'\' field is required.' }]);
-			expect(check(undefined)).toEqual([{ type: 'required', actual: undefined, message: 'The \'\' field is required.' }]);
-			expect(check(0)).toEqual(true);
-			expect(check(1)).toEqual(true);
-			expect(check('')).toEqual(true);
-			expect(check('true')).toEqual(true);
-			expect(check('false')).toEqual(true);
-			expect(check([])).toEqual(true);
-			expect(check({})).toEqual(true);
+	describe("Test rule: any", () => {
+		describe("old case (without considerNullAsAValue flag)", () => {
+			const v = new Validator();
+	
+			it("should give back true anyway", () => {
+				const check = v.compile({ $$root: true, type: "any" });
+	
+				expect(check(null)).toEqual([{ type: "required", actual: null, message: "The '' field is required." }]);
+				expect(check(undefined)).toEqual([{ type: "required", actual: undefined, message: "The '' field is required." }]);
+				expect(check(0)).toEqual(true);
+				expect(check(1)).toEqual(true);
+				expect(check("")).toEqual(true);
+				expect(check("true")).toEqual(true);
+				expect(check("false")).toEqual(true);
+				expect(check([])).toEqual(true);
+				expect(check({})).toEqual(true);
+			});
+	
+			it("should give back true anyway as optional", () => {
+				const check = v.compile({ $$root: true, type: "any", optional: true });
+	
+				expect(check(null)).toEqual(true);
+				expect(check(undefined)).toEqual(true);
+				expect(check(0)).toEqual(true);
+				expect(check(1)).toEqual(true);
+				expect(check("")).toEqual(true);
+				expect(check("true")).toEqual(true);
+				expect(check("false")).toEqual(true);
+				expect(check([])).toEqual(true);
+				expect(check({})).toEqual(true);
+			});
 		});
-
-		it('should give back true anyway', () => {
-			const check = v.compile({ $$root: true, type: 'any', optional: true });
-
-			expect(check(null)).toEqual(true);
-			expect(check(undefined)).toEqual(true);
-			expect(check(0)).toEqual(true);
-			expect(check(1)).toEqual(true);
-			expect(check('')).toEqual(true);
-			expect(check('true')).toEqual(true);
-			expect(check('false')).toEqual(true);
-			expect(check([])).toEqual(true);
-			expect(check({})).toEqual(true);
+	
+		describe("with considerNullAsAValue flag set to true", () => {
+			const v = new Validator({considerNullAsAValue: true});
+		
+			it("should give back true anyway", () => {
+				const check = v.compile({ $$root: true, type: "any" });
+		
+				expect(check(null)).toEqual(true);
+				expect(check(undefined)).toEqual([{ type: "required", actual: undefined, message: "The '' field is required." }]);
+				expect(check(0)).toEqual(true);
+				expect(check(1)).toEqual(true);
+				expect(check("")).toEqual(true);
+				expect(check("true")).toEqual(true);
+				expect(check("false")).toEqual(true);
+				expect(check([])).toEqual(true);
+				expect(check({})).toEqual(true);
+			});
+		
+			it("should give back true anyway as optional", () => {
+				const check = v.compile({ $$root: true, type: "any", optional: true });
+		
+				expect(check(null)).toEqual(true);
+				expect(check(undefined)).toEqual(true);
+				expect(check(0)).toEqual(true);
+				expect(check(1)).toEqual(true);
+				expect(check("")).toEqual(true);
+				expect(check("true")).toEqual(true);
+				expect(check("false")).toEqual(true);
+				expect(check([])).toEqual(true);
+				expect(check({})).toEqual(true);
+			});
 		});
 	});
 });
diff --git a/test/typescript/rules/array.spec.ts b/test/typescript/rules/array.spec.ts
index 71725ab..a17e00f 100644
--- a/test/typescript/rules/array.spec.ts
+++ b/test/typescript/rules/array.spec.ts
@@ -167,6 +167,25 @@ describe('TypeScript Definitions', () => {
 				expect(check(value2)).toEqual([{ type: "required", field: "data", actual: undefined, message: "The 'data' field is required." }]);
 				expect(value2.data).toEqual(undefined);
 			});
+
+			it ("should not convert into array if undefined (new case)", () => {
+				const v = new Validator({
+					useNewCustomCheckerFunction: true,
+					considerNullAsAValue: true,
+					messages: {
+						evenNumber: "The '' field must be an even number!"
+					}
+				});
+				const check = v.compile({ data: { type: "array", items: "string", convert: true } });
+				// Null check
+				const value = { data: null };
+				expect(check(value)).toEqual(true);
+				expect(value.data).toEqual(null);
+				// Undefined check
+				const value2 = { data: undefined };
+				expect(check(value2)).toEqual([{ type: "required", field: "data", actual: undefined, message: "The 'data' field is required." }]);
+				expect(value2.data).toEqual(undefined);
+			});
 		});
 	});
 });