Repository: icebob/fastest-validator (js)

Issue:
tuple item dosent support default and optioanl
default
```ts
const V = require('fastest-validator');

const v = new V();

const schema = {
  tuple: {
    type: 'tuple',
    empty: false,
    items: [{ type: 'string', default: '1' }],
  },
};

const data = { tuple: [] };
console.log(v.validate(data, schema));
console.log(data);
```

```text
[
  {
    type: 'tupleEmpty',
    message: "The 'tuple' field must not be an empty array.",
    field: 'tuple',
    actual: []
  }
]
{ tuple: [] }
```

optional
```ts
const schema = {
  tuple: {
    type: 'tuple',
    empty: false,
    items: [
      { type: 'string' },
      { type: 'number', optional: true },
    ],
  },
};

const data = { tuple: ['str', undefined] };
console.log(v.validate(data, schema));
console.log(data);
```

```text
[
  {
    type: 'number',
    message: "The 'tuple[1]' field must be a number.",
    field: 'tuple[1]',
    actual: undefined
  }
]
{ tuple: [ 'str', undefined ] }
```

Pull request description:
support default and optional in tuples and arrays
Resolves #175 
Solution: Set "value" variable to the item being validated. Similar to how properties are validated in object.js

Patch:
diff --git a/lib/rules/array.js b/lib/rules/array.js
index 4cedf0c..77f1413 100644
--- a/lib/rules/array.js
+++ b/lib/rules/array.js
@@ -78,6 +78,7 @@ module.exports = function ({ schema, messages }, path, context) {
 			var arr = value;
 			var parentField = field;
 			for (var i = 0; i < arr.length; i++) {
+				value = arr[i];
 		`);
 
 		const itemPath = path + "[]";
@@ -88,11 +89,14 @@ module.exports = function ({ schema, messages }, path, context) {
 		src.push(`
 			}
 		`);
-	}
-
-	src.push(`
+		src.push(`
+		return arr;
+	`);
+	} else {
+		src.push(`
 		return value;
 	`);
+	}
 
 	return {
 		source: src.join("\n")
diff --git a/lib/rules/tuple.js b/lib/rules/tuple.js
index 5e09a17..11251b1 100644
--- a/lib/rules/tuple.js
+++ b/lib/rules/tuple.js
@@ -52,6 +52,10 @@ module.exports = function ({ schema, messages }, path, context) {
 		`);
 
 		for (let i = 0; i < schema.items.length; i++) {
+			src.push(`
+			value = arr[${i}];
+		`);
+
 			const itemPath = `${path}[${i}]`;
 			const rule = this.getRuleFromSchema(schema.items[i]);
 			const innerSource = `
@@ -59,11 +63,14 @@ module.exports = function ({ schema, messages }, path, context) {
 		`;
 			src.push(this.compileRule(rule, context, itemPath, innerSource, `arr[${i}]`));
 		}
-	}
-
-	src.push(`
+		src.push(`
+		return arr;
+	`);
+	} else {
+		src.push(`
 		return value;
 	`);
+	}
 
 	return {
 		source: src.join("\n")


Test patch:
diff --git a/test/integration.spec.js b/test/integration.spec.js
index 1648fe5..6bc41b4 100644
--- a/test/integration.spec.js
+++ b/test/integration.spec.js
@@ -993,12 +993,16 @@ describe("Test default value sanitizer", () => {
 			name: { type: "string", default: "John" },
 			age: { type: "number", optional: true, default: 33 },
 			roles: { type: "array", items: "string", default: ["user"] },
-			status: { type: "boolean", default: true }
+			status: { type: "boolean", default: true },
+			tuple: { type: 'tuple', items: [{type: 'number', default: 666}, {type: 'string', default: 'lucifer'}] },
+			array: { type: 'array', items: {type: 'string', default: 'bar'}},
 		};
 		const check = v.compile(schema);
 		const obj = {
 			name: null,
-			status: false
+			status: false,
+			tuple: [undefined, undefined],
+			array: ['foo', undefined, 'baz']
 		};
 
 		const res = check(obj);
@@ -1009,7 +1013,9 @@ describe("Test default value sanitizer", () => {
 			name: "John",
 			age: 33,
 			roles: ["user"],
-			status: false
+			status: false,
+			tuple: [666, 'lucifer'],
+			array: ['foo', 'bar', 'baz']
 		});
 	});
 
@@ -1036,18 +1042,32 @@ describe("Test optional option", () => {
 	const v = new Validator();
 
 	it("should not throw error if value is undefined", () => {
-		const schema = { foo: { type: "number", optional: true } };
+		const schema = {
+			foo: { type: "number", optional: true },
+			array: { type: "array", optional: true, items: {type: "string", optional: true} },
+			tuple: {
+				type: "tuple",
+				optional: true,
+				items: [
+					{ type: "number", optional: true },
+				],
+			},
+		};
 		const check = v.compile(schema);
 
 		expect(check({})).toBe(true);
-		expect(check({ foo: undefined })).toBe(true);
+		expect(check({
+			foo: undefined,
+			array: [undefined],
+			tuple: [undefined]
+		})).toBe(true);
 	});
 
 	it("should not throw error if value is null", () => {
 		const schema = { foo: { type: "number", optional: true } };
 		const check = v.compile(schema);
 
-		const o = { foo: null };
+		const o = { foo: null, array: [null], tuple: [null] };
 		expect(check(o)).toBe(true);
 		expect(o.foo).toBe(null);
 	});
@@ -1060,16 +1080,31 @@ describe("Test optional option", () => {
 	});
 
 	it("should set default value if there is a default", () => {
-		const schema = { foo: { type: "number", optional: true, default: 5 } };
+		const schema = {
+			foo: { type: "number", optional: true, default: 5 },
+			array: { type: "array", optional: true, items: {type: "string", optional: true, default: "foo"} },
+			tuple: {
+				type: "tuple",
+				optional: true,
+				items: [
+					{ type: "number", optional: true, default: 666 },
+				],
+			},
+
+		};
 		const check = v.compile(schema);
 
-		const o1 = { foo: 2 };
+		const o1 = { foo: 2, array: [], tuple: [6] };
 		expect(check(o1)).toBe(true);
 		expect(o1.foo).toBe(2);
+		expect(o1.array).toStrictEqual([]);
+		expect(o1.tuple).toStrictEqual([6]);
 
-		const o2 = {};
+		const o2 = {array: [undefined], tuple: [undefined]};
 		expect(check(o2)).toBe(true);
 		expect(o2.foo).toBe(5);
+		expect(o2.array).toStrictEqual(["foo"]);
+		expect(o2.tuple).toStrictEqual([666]);
 	});
 });
 
@@ -1144,4 +1179,4 @@ describe("Test nullable option", () => {
 		expect(check(o2)).toBe(true);
 		expect(o2.foo).toBe(5);
 	});
-});
\ No newline at end of file
+});
diff --git a/test/typescript/integration.spec.ts b/test/typescript/integration.spec.ts
index df6585b..6706f51 100644
--- a/test/typescript/integration.spec.ts
+++ b/test/typescript/integration.spec.ts
@@ -1032,6 +1032,8 @@ describe('TypeScript Definitions', () => {
             age: { type: 'number', optional: true, default: 33 },
             roles: { type: 'array', items: 'string', default: ['user'] },
             status: { type: 'boolean', default: true },
+            tuple: { type: 'tuple', items: [{type: 'number', default: 666}, {type: 'string', default: 'lucifer'}] },
+			array: { type: 'array', items: {type: 'string', default: 'bar'}},
         };
         let check = v.compile(schema);
 
@@ -1039,6 +1041,8 @@ describe('TypeScript Definitions', () => {
             let obj = {
                 name: null,
                 status: false,
+                tuple: [undefined, undefined],
+				array: ['foo', undefined, 'baz']
             };
 
             let res = check(obj);
@@ -1050,6 +1054,8 @@ describe('TypeScript Definitions', () => {
                 age: 33,
                 roles: ['user'],
                 status: false,
+                tuple: [666, 'lucifer'],
+				array: ['foo', 'bar', 'baz']
             });
         });
     });
@@ -1058,18 +1064,32 @@ describe('TypeScript Definitions', () => {
         const v = new Validator();
 
         it("should not throw error if value is undefined", () => {
-            const schema = { foo: { type: "number", optional: true } };
+            const schema = {
+            	foo: { type: "number", optional: true },
+				array: { type: "array", optional: true, items: {type: "string", optional: true} },
+				tuple: {
+					type: "tuple",
+					optional: true,
+					items: [
+						{ type: "number", optional: true },
+					],
+				},
+            };
             const check = v.compile(schema);
 
             expect(check({})).toBe(true);
-            expect(check({ foo: undefined })).toBe(true);
+            expect(check({
+				foo: undefined,
+				array: [undefined],
+				tuple: [undefined]
+            })).toBe(true);
         });
 
         it("should not throw error if value is null", () => {
             const schema = { foo: { type: "number", optional: true } };
             const check = v.compile(schema);
 
-            const o = { foo: null };
+            const o = { foo: null, array: [null], tuple: [null] };
             expect(check(o)).toBe(true);
             expect(o.foo).toBe(null);
         });
@@ -1082,16 +1102,31 @@ describe('TypeScript Definitions', () => {
         });
 
         it("should set default value if there is a default", () => {
-            const schema = { foo: { type: "number", optional: true, default: 5 } };
+            const schema = {
+            	foo: { type: "number", optional: true, default: 5 },
+				array: { type: "array", optional: true, items: {type: "string", optional: true, default: "foo"} },
+				tuple: {
+					type: "tuple",
+					optional: true,
+					items: [
+						{ type: "number", optional: true, default: 666 },
+					],
+				},
+
+            };
             const check = v.compile(schema);
 
-            const o1 = { foo: 2 };
+            const o1 = { foo: 2, array: [], tuple: [6] };
             expect(check(o1)).toBe(true);
             expect(o1.foo).toBe(2);
+			expect(o1.array).toStrictEqual([]);
+			expect(o1.tuple).toStrictEqual([6]);
 
-            const o2: { foo?: number }  = {};
+            const o2: { foo?: number, array: Array<string | undefined>, tuple: [number?] } = {array: [undefined], tuple: [undefined]};
             expect(check(o2)).toBe(true);
             expect(o2.foo).toBe(5);
+			expect(o2.array).toStrictEqual(["foo"]);
+			expect(o2.tuple).toStrictEqual([666]);
         });
     });