Repository: colinhacks/zod (ts)

Issue:
discriminatedUnion on schema with transform throws an error [Zod V3]
As the title states
This snippet:
```js
import { z } from "zod";

const schema = z
  .object({
    type: z.literal("foo"),
  })
  .transform((s) => ({ ...s, v: 2 }));

const schema2 = z.object({
  type: z.literal("bar"),
});

const combinedSchema = z.discriminatedUnion("type", [schema, schema2]);
```

Throws an error

<img width="1438" height="230" alt="Image" src="https://github.com/user-attachments/assets/5def6133-e87c-4130-9bd3-275f17f027e7" />

This is on V3 

Pull request:
Support pipes in discriminated unions. Closes #4856

Patch:
diff --git a/packages/zod/src/v4/classic/compat.ts b/packages/zod/src/v4/classic/compat.ts
index 5fbabbbb..86a5813a 100644
--- a/packages/zod/src/v4/classic/compat.ts
+++ b/packages/zod/src/v4/classic/compat.ts
@@ -66,42 +66,5 @@ export type {
 /** Included for Zod 3 compatibility */
 export type ZodRawShape = core.$ZodShape;
 
-/** @deprecated Do not use. Only included for zod-to-json-schema compatibility. */
-export enum ZodFirstPartyTypeKind {
-  ZodString = "ZodString",
-  ZodNumber = "ZodNumber",
-  ZodNaN = "ZodNaN",
-  ZodBigInt = "ZodBigInt",
-  ZodBoolean = "ZodBoolean",
-  ZodDate = "ZodDate",
-  ZodSymbol = "ZodSymbol",
-  ZodUndefined = "ZodUndefined",
-  ZodNull = "ZodNull",
-  ZodAny = "ZodAny",
-  ZodUnknown = "ZodUnknown",
-  ZodNever = "ZodNever",
-  ZodVoid = "ZodVoid",
-  ZodArray = "ZodArray",
-  ZodObject = "ZodObject",
-  ZodUnion = "ZodUnion",
-  ZodDiscriminatedUnion = "ZodDiscriminatedUnion",
-  ZodIntersection = "ZodIntersection",
-  ZodTuple = "ZodTuple",
-  ZodRecord = "ZodRecord",
-  ZodMap = "ZodMap",
-  ZodSet = "ZodSet",
-  ZodFunction = "ZodFunction",
-  ZodLazy = "ZodLazy",
-  ZodLiteral = "ZodLiteral",
-  ZodEnum = "ZodEnum",
-  ZodEffects = "ZodEffects",
-  ZodNativeEnum = "ZodNativeEnum",
-  ZodOptional = "ZodOptional",
-  ZodNullable = "ZodNullable",
-  ZodDefault = "ZodDefault",
-  ZodCatch = "ZodCatch",
-  ZodPromise = "ZodPromise",
-  ZodBranded = "ZodBranded",
-  ZodPipeline = "ZodPipeline",
-  ZodReadonly = "ZodReadonly",
-}
+/** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
+export enum ZodFirstPartyTypeKind {}
diff --git a/packages/zod/src/v4/core/schemas.ts b/packages/zod/src/v4/core/schemas.ts
index 85f4e237..91de6c30 100644
--- a/packages/zod/src/v4/core/schemas.ts
+++ b/packages/zod/src/v4/core/schemas.ts
@@ -2039,7 +2039,7 @@ export const $ZodDiscriminatedUnion: core.$constructor<$ZodDiscriminatedUnion> =
       const opts = def.options as $ZodTypeDiscriminable[];
       const map: Map<util.Primitive, $ZodType> = new Map();
       for (const o of opts) {
-        const values = o._zod.propValues[def.discriminator];
+        const values = o._zod.propValues?.[def.discriminator];
         if (!values || values.size === 0)
           throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(o)}"`);
         for (const v of values) {
@@ -3429,6 +3429,7 @@ export interface $ZodPipeInternals<A extends SomeType = $ZodType, B extends Some
   values: A["_zod"]["values"];
   optin: A["_zod"]["optin"];
   optout: B["_zod"]["optout"];
+  propValues: A["_zod"]["propValues"];
 }
 
 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
   util.defineLazy(inst._zod, "values", () => def.in._zod.values);
   util.defineLazy(inst._zod, "optin", () => def.in._zod.optin);
   util.defineLazy(inst._zod, "optout", () => def.out._zod.optout);
+  util.defineLazy(inst._zod, "propValues", () => def.in._zod.propValues);
 
   inst._zod.parse = (payload, ctx) => {
     const left = def.in._zod.run(payload, ctx);
diff --git a/play.ts b/play.ts
index 8e3287f6..bf75b638 100644
--- a/play.ts
+++ b/play.ts
@@ -1,7 +1,8 @@
-import { es } from "zod/locales";
-// import type * as z from "zod";
-import * as z from "zod/mini";
+import { z } from "zod";
 
-z.config(es());
+const Parent = z.object({ key1: z.object({ key2: z.string() }) });
 
-z.string().parse(123);
+console.log(Parent._zod.def.shape.key1._zod.run);
+// => [Function (anonymous)]
+
+console.log(z.parse(Parent._zod.def.shape.key1, { key2: "asdf" }));


Test patch:
diff --git a/packages/zod/src/v4/classic/tests/discriminated-unions.test.ts b/packages/zod/src/v4/classic/tests/discriminated-unions.test.ts
index 9aba3df8..dfdf7197 100644
--- a/packages/zod/src/v4/classic/tests/discriminated-unions.test.ts
+++ b/packages/zod/src/v4/classic/tests/discriminated-unions.test.ts
@@ -617,3 +617,32 @@ test("readonly literal discriminator", () => {
     discUnion.parse({ type: "c", a: "hello" });
   }).toThrow();
 });
+
+test("pipes", () => {
+  const schema = z
+    .object({
+      type: z.literal("foo"),
+    })
+    .transform((s) => ({ ...s, v: 2 }));
+
+  expect(schema._zod.propValues).toMatchInlineSnapshot(`
+    {
+      "type": Set {
+        "foo",
+      },
+    }
+  `);
+
+  const schema2 = z.object({
+    type: z.literal("bar"),
+  });
+
+  const combinedSchema = z.discriminatedUnion("type", [schema, schema2], {
+    unionFallback: false,
+  });
+
+  combinedSchema.parse({
+    type: "foo",
+    v: 2,
+  });
+});