From 77b79eff5e51d35d728f4d956ce6fc8f4a034d9a Mon Sep 17 00:00:00 2001 From: Robin Labat Date: Tue, 26 Jul 2022 17:30:11 +0200 Subject: [PATCH 01/10] #1171 --- .../lib/__tests__/discriminatedUnions.test.ts | 9 +++++ deno/lib/types.ts | 35 +++++++++++++++++-- src/__tests__/discriminatedUnions.test.ts | 9 +++++ src/types.ts | 35 +++++++++++++++++-- 4 files changed, 82 insertions(+), 6 deletions(-) diff --git a/deno/lib/__tests__/discriminatedUnions.test.ts b/deno/lib/__tests__/discriminatedUnions.test.ts index a5f8f8996..c61cb54a4 100644 --- a/deno/lib/__tests__/discriminatedUnions.test.ts +++ b/deno/lib/__tests__/discriminatedUnions.test.ts @@ -25,6 +25,11 @@ test("valid - discriminator value of various primitive types", () => { z.object({ type: z.literal(null), val: z.literal(7) }), z.object({ type: z.literal("undefined"), val: z.literal(8) }), z.object({ type: z.literal(undefined), val: z.literal(9) }), + z + .object({ type: z.literal("transform"), val: z.literal(10) }) + .transform((val) => ({ + val, + })), ]); expect(schema.parse({ type: "1", val: 1 })).toEqual({ type: "1", val: 1 }); @@ -57,6 +62,10 @@ test("valid - discriminator value of various primitive types", () => { type: undefined, val: 9, }); + // console.log("test"); + // expect(schema.parse({ type: "transform", val: 10 })).toEqual({ + // val: 10, + // }); }); test("invalid - null", () => { diff --git a/deno/lib/types.ts b/deno/lib/types.ts index 13f6eaaa9..3024d6a97 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -2137,7 +2137,7 @@ export class ZodUnion extends ZodType< ///////////////////////////////////////////////////// ///////////////////////////////////////////////////// -export type ZodDiscriminatedUnionOption< +export type ZodDiscriminatedUnionOptionBase< Discriminator extends string, DiscriminatorValue extends Primitive > = ZodObject< @@ -2146,6 +2146,15 @@ export type ZodDiscriminatedUnionOption< any >; +export type ZodDiscriminatedUnionOption< + Discriminator extends string, + DiscriminatorValue extends Primitive +> = + | ZodDiscriminatedUnionOptionBase + | ZodEffects< + ZodDiscriminatedUnionOptionBase + >; + export interface ZodDiscriminatedUnionDef< Discriminator extends string, DiscriminatorValue extends Primitive, @@ -2243,8 +2252,22 @@ export class ZodDiscriminatedUnion< try { types.forEach((type) => { - const discriminatorValue = type.shape[discriminator].value; - options.set(discriminatorValue, type); + if (type._def.typeName === ZodFirstPartyTypeKind.ZodObject) { + const discriminatorValue = ( + type as ZodDiscriminatedUnionOptionBase< + Discriminator, + DiscriminatorValue + > + ).shape[discriminator].value; + options.set(discriminatorValue, type); + } else if (type._def.typeName === ZodFirstPartyTypeKind.ZodEffects) { + const discriminatorValue = ( + type as ZodEffects< + ZodDiscriminatedUnionOptionBase + > + ).sourceType().shape[discriminator].value; + options.set(discriminatorValue, type); + } }); } catch (e) { throw new Error( @@ -3417,6 +3440,12 @@ export class ZodEffects< return this._def.schema; } + sourceType(): T { + return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects + ? (this._def.schema as unknown as ZodEffects).sourceType() + : (this._def.schema as T); + } + _parse(input: ParseInput): ParseReturnType { const { status, ctx } = this._processInputParams(input); diff --git a/src/__tests__/discriminatedUnions.test.ts b/src/__tests__/discriminatedUnions.test.ts index 3d26a14d9..dc6e70814 100644 --- a/src/__tests__/discriminatedUnions.test.ts +++ b/src/__tests__/discriminatedUnions.test.ts @@ -24,6 +24,11 @@ test("valid - discriminator value of various primitive types", () => { z.object({ type: z.literal(null), val: z.literal(7) }), z.object({ type: z.literal("undefined"), val: z.literal(8) }), z.object({ type: z.literal(undefined), val: z.literal(9) }), + z + .object({ type: z.literal("transform"), val: z.literal(10) }) + .transform((val) => ({ + val, + })), ]); expect(schema.parse({ type: "1", val: 1 })).toEqual({ type: "1", val: 1 }); @@ -56,6 +61,10 @@ test("valid - discriminator value of various primitive types", () => { type: undefined, val: 9, }); + // console.log("test"); + // expect(schema.parse({ type: "transform", val: 10 })).toEqual({ + // val: 10, + // }); }); test("invalid - null", () => { diff --git a/src/types.ts b/src/types.ts index 1d4badcdd..2518f083f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2137,7 +2137,7 @@ export class ZodUnion extends ZodType< ///////////////////////////////////////////////////// ///////////////////////////////////////////////////// -export type ZodDiscriminatedUnionOption< +export type ZodDiscriminatedUnionOptionBase< Discriminator extends string, DiscriminatorValue extends Primitive > = ZodObject< @@ -2146,6 +2146,15 @@ export type ZodDiscriminatedUnionOption< any >; +export type ZodDiscriminatedUnionOption< + Discriminator extends string, + DiscriminatorValue extends Primitive +> = + | ZodDiscriminatedUnionOptionBase + | ZodEffects< + ZodDiscriminatedUnionOptionBase + >; + export interface ZodDiscriminatedUnionDef< Discriminator extends string, DiscriminatorValue extends Primitive, @@ -2243,8 +2252,22 @@ export class ZodDiscriminatedUnion< try { types.forEach((type) => { - const discriminatorValue = type.shape[discriminator].value; - options.set(discriminatorValue, type); + if (type._def.typeName === ZodFirstPartyTypeKind.ZodObject) { + const discriminatorValue = ( + type as ZodDiscriminatedUnionOptionBase< + Discriminator, + DiscriminatorValue + > + ).shape[discriminator].value; + options.set(discriminatorValue, type); + } else if (type._def.typeName === ZodFirstPartyTypeKind.ZodEffects) { + const discriminatorValue = ( + type as ZodEffects< + ZodDiscriminatedUnionOptionBase + > + ).sourceType().shape[discriminator].value; + options.set(discriminatorValue, type); + } }); } catch (e) { throw new Error( @@ -3417,6 +3440,12 @@ export class ZodEffects< return this._def.schema; } + sourceType(): T { + return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects + ? (this._def.schema as unknown as ZodEffects).sourceType() + : (this._def.schema as T); + } + _parse(input: ParseInput): ParseReturnType { const { status, ctx } = this._processInputParams(input); From 9b0d9f22b08af6318fa407d50193003abe87055d Mon Sep 17 00:00:00 2001 From: Robin Labat Date: Tue, 26 Jul 2022 17:39:48 +0200 Subject: [PATCH 02/10] fix tests --- deno/lib/__tests__/discriminatedUnions.test.ts | 16 +++++++++++----- src/__tests__/discriminatedUnions.test.ts | 16 +++++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/deno/lib/__tests__/discriminatedUnions.test.ts b/deno/lib/__tests__/discriminatedUnions.test.ts index c61cb54a4..57b98a013 100644 --- a/deno/lib/__tests__/discriminatedUnions.test.ts +++ b/deno/lib/__tests__/discriminatedUnions.test.ts @@ -28,8 +28,11 @@ test("valid - discriminator value of various primitive types", () => { z .object({ type: z.literal("transform"), val: z.literal(10) }) .transform((val) => ({ - val, + val: val.val, })), + z + .object({ type: z.literal("refine"), val: z.literal(11) }) + .refine(() => true), ]); expect(schema.parse({ type: "1", val: 1 })).toEqual({ type: "1", val: 1 }); @@ -62,10 +65,13 @@ test("valid - discriminator value of various primitive types", () => { type: undefined, val: 9, }); - // console.log("test"); - // expect(schema.parse({ type: "transform", val: 10 })).toEqual({ - // val: 10, - // }); + expect(schema.parse({ type: "transform", val: 10 })).toEqual({ + val: 10, + }); + expect(schema.parse({ type: "refine", val: 11 })).toEqual({ + type: "refine", + val: 11, + }); }); test("invalid - null", () => { diff --git a/src/__tests__/discriminatedUnions.test.ts b/src/__tests__/discriminatedUnions.test.ts index dc6e70814..eb1d02bfe 100644 --- a/src/__tests__/discriminatedUnions.test.ts +++ b/src/__tests__/discriminatedUnions.test.ts @@ -27,8 +27,11 @@ test("valid - discriminator value of various primitive types", () => { z .object({ type: z.literal("transform"), val: z.literal(10) }) .transform((val) => ({ - val, + val: val.val, })), + z + .object({ type: z.literal("refine"), val: z.literal(11) }) + .refine(() => true), ]); expect(schema.parse({ type: "1", val: 1 })).toEqual({ type: "1", val: 1 }); @@ -61,10 +64,13 @@ test("valid - discriminator value of various primitive types", () => { type: undefined, val: 9, }); - // console.log("test"); - // expect(schema.parse({ type: "transform", val: 10 })).toEqual({ - // val: 10, - // }); + expect(schema.parse({ type: "transform", val: 10 })).toEqual({ + val: 10, + }); + expect(schema.parse({ type: "refine", val: 11 })).toEqual({ + type: "refine", + val: 11, + }); }); test("invalid - null", () => { From 668bd5d5043c8726ed36b5118670bb508ce0b671 Mon Sep 17 00:00:00 2001 From: Robin Labat Date: Tue, 26 Jul 2022 17:41:39 +0200 Subject: [PATCH 03/10] add superRefine in tests --- deno/lib/__tests__/discriminatedUnions.test.ts | 7 +++++++ src/__tests__/discriminatedUnions.test.ts | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/deno/lib/__tests__/discriminatedUnions.test.ts b/deno/lib/__tests__/discriminatedUnions.test.ts index 57b98a013..d338abc62 100644 --- a/deno/lib/__tests__/discriminatedUnions.test.ts +++ b/deno/lib/__tests__/discriminatedUnions.test.ts @@ -33,6 +33,9 @@ test("valid - discriminator value of various primitive types", () => { z .object({ type: z.literal("refine"), val: z.literal(11) }) .refine(() => true), + z + .object({ type: z.literal("superRefine"), val: z.literal(12) }) + .superRefine(() => {}), ]); expect(schema.parse({ type: "1", val: 1 })).toEqual({ type: "1", val: 1 }); @@ -72,6 +75,10 @@ test("valid - discriminator value of various primitive types", () => { type: "refine", val: 11, }); + expect(schema.parse({ type: "superRefine", val: 12 })).toEqual({ + type: "superRefine", + val: 12, + }); }); test("invalid - null", () => { diff --git a/src/__tests__/discriminatedUnions.test.ts b/src/__tests__/discriminatedUnions.test.ts index eb1d02bfe..b119f5589 100644 --- a/src/__tests__/discriminatedUnions.test.ts +++ b/src/__tests__/discriminatedUnions.test.ts @@ -32,6 +32,9 @@ test("valid - discriminator value of various primitive types", () => { z .object({ type: z.literal("refine"), val: z.literal(11) }) .refine(() => true), + z + .object({ type: z.literal("superRefine"), val: z.literal(12) }) + .superRefine(() => {}), ]); expect(schema.parse({ type: "1", val: 1 })).toEqual({ type: "1", val: 1 }); @@ -71,6 +74,10 @@ test("valid - discriminator value of various primitive types", () => { type: "refine", val: 11, }); + expect(schema.parse({ type: "superRefine", val: 12 })).toEqual({ + type: "superRefine", + val: 12, + }); }); test("invalid - null", () => { From 87883fa354a59b57d40b66dd2fbf268fa7754ac1 Mon Sep 17 00:00:00 2001 From: Robin Labat Date: Wed, 27 Jul 2022 12:00:08 +0200 Subject: [PATCH 04/10] add support for lazy --- deno/lib/README.md | 2 +- .../lib/__tests__/discriminatedUnions.test.ts | 25 ++++++++ deno/lib/types.ts | 59 +++++++++++-------- src/__tests__/discriminatedUnions.test.ts | 25 ++++++++ src/types.ts | 59 +++++++++++-------- 5 files changed, 123 insertions(+), 47 deletions(-) diff --git a/deno/lib/README.md b/deno/lib/README.md index 2b6c41743..856f8c633 100644 --- a/deno/lib/README.md +++ b/deno/lib/README.md @@ -604,7 +604,7 @@ dateSchema.safeParse(new Date("1/12/22")); // success: true dateSchema.safeParse("2022-01-12T00:00:00.000Z"); // success: true ``` -## Zod enums- +## Zod enums ```ts const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]); diff --git a/deno/lib/__tests__/discriminatedUnions.test.ts b/deno/lib/__tests__/discriminatedUnions.test.ts index d338abc62..45ecfa176 100644 --- a/deno/lib/__tests__/discriminatedUnions.test.ts +++ b/deno/lib/__tests__/discriminatedUnions.test.ts @@ -36,6 +36,21 @@ test("valid - discriminator value of various primitive types", () => { z .object({ type: z.literal("superRefine"), val: z.literal(12) }) .superRefine(() => {}), + z.lazy(() => z.object({ type: z.literal("lazy"), val: z.literal(13) })), + z.lazy(() => + z + .object({ type: z.literal("chained 1"), val: z.literal(14) }) + .transform((val) => ({ + val: val.val, + })) + ), + z + .lazy(() => + z.object({ type: z.literal("chained 2"), val: z.literal(15) }) + ) + .transform((val) => ({ + val: val.val, + })), ]); expect(schema.parse({ type: "1", val: 1 })).toEqual({ type: "1", val: 1 }); @@ -79,6 +94,16 @@ test("valid - discriminator value of various primitive types", () => { type: "superRefine", val: 12, }); + expect(schema.parse({ type: "lazy", val: 13 })).toEqual({ + type: "lazy", + val: 13, + }); + expect(schema.parse({ type: "chained 1", val: 14 })).toEqual({ + val: 14, + }); + expect(schema.parse({ type: "chained 2", val: 15 })).toEqual({ + val: 15, + }); }); test("invalid - null", () => { diff --git a/deno/lib/types.ts b/deno/lib/types.ts index 3024d6a97..0305650fc 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -18,7 +18,7 @@ import { ParsePath, ParseReturnType, ParseStatus, - SyncParseReturnType, + SyncParseReturnType } from "./helpers/parseUtil.ts"; import { partialUtil } from "./helpers/partialUtil.ts"; import { Primitive } from "./helpers/typeAliases.ts"; @@ -30,7 +30,7 @@ import { ZodError, ZodErrorMap, ZodIssue, - ZodIssueCode, + ZodIssueCode } from "./ZodError.ts"; /////////////////////////////////////// @@ -2137,6 +2137,34 @@ export class ZodUnion extends ZodType< ///////////////////////////////////////////////////// ///////////////////////////////////////////////////// +type ZodSourceType = T extends ZodLazy + ? ZodSourceType + : T extends ZodEffects + ? ZodSourceType + : T; + +type ZodOriginType = + | T + | ZodLazy + | ZodEffects + | ZodLazy> + | ZodEffects>; + +function getSourceType(type: T): ZodSourceType { + if (type._def.typeName === ZodFirstPartyTypeKind.ZodLazy) { + console.log("lazy"); + return getSourceType( + (type as unknown as ZodLazy).schema + ) as ZodSourceType; + } else if (type._def.typeName === ZodFirstPartyTypeKind.ZodEffects) { + return getSourceType( + (type as unknown as ZodEffects).sourceType() + ) as ZodSourceType; + } else { + return type as ZodSourceType; + } +} + export type ZodDiscriminatedUnionOptionBase< Discriminator extends string, DiscriminatorValue extends Primitive @@ -2149,11 +2177,9 @@ export type ZodDiscriminatedUnionOptionBase< export type ZodDiscriminatedUnionOption< Discriminator extends string, DiscriminatorValue extends Primitive -> = - | ZodDiscriminatedUnionOptionBase - | ZodEffects< - ZodDiscriminatedUnionOptionBase - >; +> = ZodOriginType< + ZodDiscriminatedUnionOptionBase +>; export interface ZodDiscriminatedUnionDef< Discriminator extends string, @@ -2252,22 +2278,9 @@ export class ZodDiscriminatedUnion< try { types.forEach((type) => { - if (type._def.typeName === ZodFirstPartyTypeKind.ZodObject) { - const discriminatorValue = ( - type as ZodDiscriminatedUnionOptionBase< - Discriminator, - DiscriminatorValue - > - ).shape[discriminator].value; - options.set(discriminatorValue, type); - } else if (type._def.typeName === ZodFirstPartyTypeKind.ZodEffects) { - const discriminatorValue = ( - type as ZodEffects< - ZodDiscriminatedUnionOptionBase - > - ).sourceType().shape[discriminator].value; - options.set(discriminatorValue, type); - } + const discriminatorValue = + getSourceType(type).shape[discriminator].value; + options.set(discriminatorValue, type); }); } catch (e) { throw new Error( diff --git a/src/__tests__/discriminatedUnions.test.ts b/src/__tests__/discriminatedUnions.test.ts index b119f5589..3ea304918 100644 --- a/src/__tests__/discriminatedUnions.test.ts +++ b/src/__tests__/discriminatedUnions.test.ts @@ -35,6 +35,21 @@ test("valid - discriminator value of various primitive types", () => { z .object({ type: z.literal("superRefine"), val: z.literal(12) }) .superRefine(() => {}), + z.lazy(() => z.object({ type: z.literal("lazy"), val: z.literal(13) })), + z.lazy(() => + z + .object({ type: z.literal("chained 1"), val: z.literal(14) }) + .transform((val) => ({ + val: val.val, + })) + ), + z + .lazy(() => + z.object({ type: z.literal("chained 2"), val: z.literal(15) }) + ) + .transform((val) => ({ + val: val.val, + })), ]); expect(schema.parse({ type: "1", val: 1 })).toEqual({ type: "1", val: 1 }); @@ -78,6 +93,16 @@ test("valid - discriminator value of various primitive types", () => { type: "superRefine", val: 12, }); + expect(schema.parse({ type: "lazy", val: 13 })).toEqual({ + type: "lazy", + val: 13, + }); + expect(schema.parse({ type: "chained 1", val: 14 })).toEqual({ + val: 14, + }); + expect(schema.parse({ type: "chained 2", val: 15 })).toEqual({ + val: 15, + }); }); test("invalid - null", () => { diff --git a/src/types.ts b/src/types.ts index 2518f083f..897a44b87 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,7 +18,7 @@ import { ParsePath, ParseReturnType, ParseStatus, - SyncParseReturnType, + SyncParseReturnType } from "./helpers/parseUtil"; import { partialUtil } from "./helpers/partialUtil"; import { Primitive } from "./helpers/typeAliases"; @@ -30,7 +30,7 @@ import { ZodError, ZodErrorMap, ZodIssue, - ZodIssueCode, + ZodIssueCode } from "./ZodError"; /////////////////////////////////////// @@ -2137,6 +2137,34 @@ export class ZodUnion extends ZodType< ///////////////////////////////////////////////////// ///////////////////////////////////////////////////// +type ZodSourceType = T extends ZodLazy + ? ZodSourceType + : T extends ZodEffects + ? ZodSourceType + : T; + +type ZodOriginType = + | T + | ZodLazy + | ZodEffects + | ZodLazy> + | ZodEffects>; + +function getSourceType(type: T): ZodSourceType { + if (type._def.typeName === ZodFirstPartyTypeKind.ZodLazy) { + console.log("lazy"); + return getSourceType( + (type as unknown as ZodLazy).schema + ) as ZodSourceType; + } else if (type._def.typeName === ZodFirstPartyTypeKind.ZodEffects) { + return getSourceType( + (type as unknown as ZodEffects).sourceType() + ) as ZodSourceType; + } else { + return type as ZodSourceType; + } +} + export type ZodDiscriminatedUnionOptionBase< Discriminator extends string, DiscriminatorValue extends Primitive @@ -2149,11 +2177,9 @@ export type ZodDiscriminatedUnionOptionBase< export type ZodDiscriminatedUnionOption< Discriminator extends string, DiscriminatorValue extends Primitive -> = - | ZodDiscriminatedUnionOptionBase - | ZodEffects< - ZodDiscriminatedUnionOptionBase - >; +> = ZodOriginType< + ZodDiscriminatedUnionOptionBase +>; export interface ZodDiscriminatedUnionDef< Discriminator extends string, @@ -2252,22 +2278,9 @@ export class ZodDiscriminatedUnion< try { types.forEach((type) => { - if (type._def.typeName === ZodFirstPartyTypeKind.ZodObject) { - const discriminatorValue = ( - type as ZodDiscriminatedUnionOptionBase< - Discriminator, - DiscriminatorValue - > - ).shape[discriminator].value; - options.set(discriminatorValue, type); - } else if (type._def.typeName === ZodFirstPartyTypeKind.ZodEffects) { - const discriminatorValue = ( - type as ZodEffects< - ZodDiscriminatedUnionOptionBase - > - ).sourceType().shape[discriminator].value; - options.set(discriminatorValue, type); - } + const discriminatorValue = + getSourceType(type).shape[discriminator].value; + options.set(discriminatorValue, type); }); } catch (e) { throw new Error( From 9b591c0d2ddb5121cf5622d93f1a8599182c158b Mon Sep 17 00:00:00 2001 From: Robin Labat Date: Wed, 27 Jul 2022 17:32:37 +0200 Subject: [PATCH 05/10] fix typings --- .../lib/__tests__/discriminatedUnions.test.ts | 11 +++++++++ deno/lib/types.ts | 24 +++++++++++-------- src/__tests__/discriminatedUnions.test.ts | 11 +++++++++ src/types.ts | 24 +++++++++++-------- 4 files changed, 50 insertions(+), 20 deletions(-) diff --git a/deno/lib/__tests__/discriminatedUnions.test.ts b/deno/lib/__tests__/discriminatedUnions.test.ts index 45ecfa176..1aa2702b9 100644 --- a/deno/lib/__tests__/discriminatedUnions.test.ts +++ b/deno/lib/__tests__/discriminatedUnions.test.ts @@ -51,6 +51,14 @@ test("valid - discriminator value of various primitive types", () => { .transform((val) => ({ val: val.val, })), + z + .lazy(() => + z.object({ type: z.literal("chained 3"), val: z.literal(16) }) + ) + .transform((val) => ({ + val: val.val, + })) + .refine(() => true), ]); expect(schema.parse({ type: "1", val: 1 })).toEqual({ type: "1", val: 1 }); @@ -104,6 +112,9 @@ test("valid - discriminator value of various primitive types", () => { expect(schema.parse({ type: "chained 2", val: 15 })).toEqual({ val: 15, }); + expect(schema.parse({ type: "chained 3", val: 16 })).toEqual({ + val: 16, + }); }); test("invalid - null", () => { diff --git a/deno/lib/types.ts b/deno/lib/types.ts index 0305650fc..0ca4369f1 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -2143,12 +2143,16 @@ type ZodSourceType = T extends ZodLazy ? ZodSourceType : T; -type ZodOriginType = - | T - | ZodLazy - | ZodEffects - | ZodLazy> - | ZodEffects>; +type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + +type ZodOriginType = [ + D +] extends [never] + ? never + : + | T + | ZodEffects> + | ZodLazy>; function getSourceType(type: T): ZodSourceType { if (type._def.typeName === ZodFirstPartyTypeKind.ZodLazy) { @@ -2196,9 +2200,9 @@ export class ZodDiscriminatedUnion< DiscriminatorValue extends Primitive, Option extends ZodDiscriminatedUnionOption > extends ZodType< - Option["_output"], + output