From bcaaf739602e3da85026612db12c9ac2442cf37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20Rodzi=C5=84ski?= Date: Sat, 24 Dec 2022 09:32:08 +0100 Subject: [PATCH] fix: [#1693] Tuple with empty items (#1712) * Add testcase - tuple as sparse array * Destructure array to make .map() work properly - ZodTuple * Make tuple as sparse array test more clear * Add testcase - array as sparse array * Destructure array to make .map() work properly - ZodArray --- deno/lib/__tests__/array.test.ts | 6 ++++++ deno/lib/__tests__/tuple.test.ts | 4 ++++ deno/lib/types.ts | 2 +- src/__tests__/array.test.ts | 6 ++++++ src/__tests__/tuple.test.ts | 4 ++++ src/types.ts | 6 +++--- 6 files changed, 24 insertions(+), 4 deletions(-) diff --git a/deno/lib/__tests__/array.test.ts b/deno/lib/__tests__/array.test.ts index 6fe0f335f..8aed9d63f 100644 --- a/deno/lib/__tests__/array.test.ts +++ b/deno/lib/__tests__/array.test.ts @@ -64,3 +64,9 @@ test("continue parsing despite array size error", () => { expect(result.error.issues.length).toEqual(2); } }); + +test("parse should fail given sparse array", () => { + const schema = z.array(z.string()).nonempty().min(1).max(3); + + expect(() => schema.parse(new Array(3))).toThrow(); +}); diff --git a/deno/lib/__tests__/tuple.test.ts b/deno/lib/__tests__/tuple.test.ts index 2271df9d9..77d1845c0 100644 --- a/deno/lib/__tests__/tuple.test.ts +++ b/deno/lib/__tests__/tuple.test.ts @@ -89,6 +89,10 @@ test("tuple with rest schema", () => { util.assertEqual(true); }); +test("parse should fail given sparse array as tuple", () => { + expect(() => testTuple.parse(new Array(3))).toThrow(); +}); + // test('tuple with optional elements', () => { // const result = z // .tuple([z.string(), z.number().optional()]) diff --git a/deno/lib/types.ts b/deno/lib/types.ts index 8dd80a0f1..7a6176d70 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -2851,7 +2851,7 @@ export class ZodTuple< status.dirty(); } - const items = (ctx.data as any[]) + const items = ([...ctx.data] as any[]) .map((item, itemIndex) => { const schema = this._def.items[itemIndex] || this._def.rest; if (!schema) return null as any as SyncParseReturnType; diff --git a/src/__tests__/array.test.ts b/src/__tests__/array.test.ts index 00ae06618..21ae44bee 100644 --- a/src/__tests__/array.test.ts +++ b/src/__tests__/array.test.ts @@ -63,3 +63,9 @@ test("continue parsing despite array size error", () => { expect(result.error.issues.length).toEqual(2); } }); + +test("parse should fail given sparse array", () => { + const schema = z.array(z.string()).nonempty().min(1).max(3); + + expect(() => schema.parse(new Array(3))).toThrow(); +}); diff --git a/src/__tests__/tuple.test.ts b/src/__tests__/tuple.test.ts index a1e4aa50e..364881c4e 100644 --- a/src/__tests__/tuple.test.ts +++ b/src/__tests__/tuple.test.ts @@ -88,6 +88,10 @@ test("tuple with rest schema", () => { util.assertEqual(true); }); +test("parse should fail given sparse array as tuple", () => { + expect(() => testTuple.parse(new Array(3))).toThrow(); +}); + // test('tuple with optional elements', () => { // const result = z // .tuple([z.string(), z.number().optional()]) diff --git a/src/types.ts b/src/types.ts index 5c7da90c4..3d26ad771 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1713,7 +1713,7 @@ export class ZodArray< if (ctx.common.async) { return Promise.all( - (ctx.data as any[]).map((item, i) => { + ([...ctx.data] as any[]).map((item, i) => { return def.type._parseAsync( new ParseInputLazyPath(ctx, item, ctx.path, i) ); @@ -1723,7 +1723,7 @@ export class ZodArray< }); } - const result = (ctx.data as any[]).map((item, i) => { + const result = ([...ctx.data] as any[]).map((item, i) => { return def.type._parseSync( new ParseInputLazyPath(ctx, item, ctx.path, i) ); @@ -2851,7 +2851,7 @@ export class ZodTuple< status.dirty(); } - const items = (ctx.data as any[]) + const items = ([...ctx.data] as any[]) .map((item, itemIndex) => { const schema = this._def.items[itemIndex] || this._def.rest; if (!schema) return null as any as SyncParseReturnType;