Skip to content

Commit f91a73e

Browse files
authored
Support pipes in discriminated unions. Closes #4856 (#4861)
1 parent 9335f05 commit f91a73e

File tree

4 files changed

+40
-45
lines changed

4 files changed

+40
-45
lines changed

packages/zod/src/v4/classic/compat.ts

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -66,42 +66,5 @@ export type {
6666
/** Included for Zod 3 compatibility */
6767
export type ZodRawShape = core.$ZodShape;
6868

69-
/** @deprecated Do not use. Only included for zod-to-json-schema compatibility. */
70-
export enum ZodFirstPartyTypeKind {
71-
ZodString = "ZodString",
72-
ZodNumber = "ZodNumber",
73-
ZodNaN = "ZodNaN",
74-
ZodBigInt = "ZodBigInt",
75-
ZodBoolean = "ZodBoolean",
76-
ZodDate = "ZodDate",
77-
ZodSymbol = "ZodSymbol",
78-
ZodUndefined = "ZodUndefined",
79-
ZodNull = "ZodNull",
80-
ZodAny = "ZodAny",
81-
ZodUnknown = "ZodUnknown",
82-
ZodNever = "ZodNever",
83-
ZodVoid = "ZodVoid",
84-
ZodArray = "ZodArray",
85-
ZodObject = "ZodObject",
86-
ZodUnion = "ZodUnion",
87-
ZodDiscriminatedUnion = "ZodDiscriminatedUnion",
88-
ZodIntersection = "ZodIntersection",
89-
ZodTuple = "ZodTuple",
90-
ZodRecord = "ZodRecord",
91-
ZodMap = "ZodMap",
92-
ZodSet = "ZodSet",
93-
ZodFunction = "ZodFunction",
94-
ZodLazy = "ZodLazy",
95-
ZodLiteral = "ZodLiteral",
96-
ZodEnum = "ZodEnum",
97-
ZodEffects = "ZodEffects",
98-
ZodNativeEnum = "ZodNativeEnum",
99-
ZodOptional = "ZodOptional",
100-
ZodNullable = "ZodNullable",
101-
ZodDefault = "ZodDefault",
102-
ZodCatch = "ZodCatch",
103-
ZodPromise = "ZodPromise",
104-
ZodBranded = "ZodBranded",
105-
ZodPipeline = "ZodPipeline",
106-
ZodReadonly = "ZodReadonly",
107-
}
69+
/** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
70+
export enum ZodFirstPartyTypeKind {}

packages/zod/src/v4/classic/tests/discriminated-unions.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,3 +617,32 @@ test("readonly literal discriminator", () => {
617617
discUnion.parse({ type: "c", a: "hello" });
618618
}).toThrow();
619619
});
620+
621+
test("pipes", () => {
622+
const schema = z
623+
.object({
624+
type: z.literal("foo"),
625+
})
626+
.transform((s) => ({ ...s, v: 2 }));
627+
628+
expect(schema._zod.propValues).toMatchInlineSnapshot(`
629+
{
630+
"type": Set {
631+
"foo",
632+
},
633+
}
634+
`);
635+
636+
const schema2 = z.object({
637+
type: z.literal("bar"),
638+
});
639+
640+
const combinedSchema = z.discriminatedUnion("type", [schema, schema2], {
641+
unionFallback: false,
642+
});
643+
644+
combinedSchema.parse({
645+
type: "foo",
646+
v: 2,
647+
});
648+
});

packages/zod/src/v4/core/schemas.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2039,7 +2039,7 @@ export const $ZodDiscriminatedUnion: core.$constructor<$ZodDiscriminatedUnion> =
20392039
const opts = def.options as $ZodTypeDiscriminable[];
20402040
const map: Map<util.Primitive, $ZodType> = new Map();
20412041
for (const o of opts) {
2042-
const values = o._zod.propValues[def.discriminator];
2042+
const values = o._zod.propValues?.[def.discriminator];
20432043
if (!values || values.size === 0)
20442044
throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(o)}"`);
20452045
for (const v of values) {
@@ -3429,6 +3429,7 @@ export interface $ZodPipeInternals<A extends SomeType = $ZodType, B extends Some
34293429
values: A["_zod"]["values"];
34303430
optin: A["_zod"]["optin"];
34313431
optout: B["_zod"]["optout"];
3432+
propValues: A["_zod"]["propValues"];
34323433
}
34333434

34343435
export interface $ZodPipe<A extends SomeType = $ZodType, B extends SomeType = $ZodType> extends $ZodType {
@@ -3440,6 +3441,7 @@ export const $ZodPipe: core.$constructor<$ZodPipe> = /*@__PURE__*/ core.$constru
34403441
util.defineLazy(inst._zod, "values", () => def.in._zod.values);
34413442
util.defineLazy(inst._zod, "optin", () => def.in._zod.optin);
34423443
util.defineLazy(inst._zod, "optout", () => def.out._zod.optout);
3444+
util.defineLazy(inst._zod, "propValues", () => def.in._zod.propValues);
34433445

34443446
inst._zod.parse = (payload, ctx) => {
34453447
const left = def.in._zod.run(payload, ctx);

play.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { es } from "zod/locales";
2-
// import type * as z from "zod";
3-
import * as z from "zod/mini";
1+
import { z } from "zod";
42

5-
z.config(es());
3+
const Parent = z.object({ key1: z.object({ key2: z.string() }) });
64

7-
z.string().parse(123);
5+
console.log(Parent._zod.def.shape.key1._zod.run);
6+
// => [Function (anonymous)]
7+
8+
console.log(z.parse(Parent._zod.def.shape.key1, { key2: "asdf" }));

0 commit comments

Comments
 (0)