From 462d113e037f44374b61d22d102a9b42455c00b5 Mon Sep 17 00:00:00 2001 From: Nicolas Ettlin Date: Thu, 4 Dec 2025 14:11:21 -0800 Subject: [PATCH 1/2] fix? --- .../server/zod4.convextozod.test.ts | 38 +++++++++++++++++++ packages/convex-helpers/server/zod4.ts | 20 ++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/packages/convex-helpers/server/zod4.convextozod.test.ts b/packages/convex-helpers/server/zod4.convextozod.test.ts index 5032628e..985a9fcb 100644 --- a/packages/convex-helpers/server/zod4.convextozod.test.ts +++ b/packages/convex-helpers/server/zod4.convextozod.test.ts @@ -7,10 +7,14 @@ import { Infer, v, VFloat64, + VLiteral, VString, + VUnion, } from "convex/values"; import { convexToZod, Zid, zid, ZodValidatorFromConvex } from "./zod4"; import { isSameType } from "zod-compare/zod4"; +import { literals } from "../validators"; +import { ignoreUnionOrder } from "./zod4.zodtoconvex.test"; test("Zid is a record key", () => { const myZid = zid("users"); @@ -205,6 +209,40 @@ describe("convexToZod", () => { }); }); }); + + // https://github.com/get-convex/convex-helpers/issues/861 + test("regression: literals helper", () => { + testConvexToZod( + v.object({ + firstName: v.string(), + lastName: v.string(), + gender: literals("male", "female", "other"), + }), + z.object({ + firstName: z.string(), + lastName: z.string(), + gender: z.union([ + z.literal("male"), + z.literal("female"), + z.literal("other"), + ]), + }), + ); + }); + + // https://github.com/get-convex/convex-helpers/issues/861#issuecomment-3593231904 + test("regression: spreading an enum into v.union", () => { + enum Gender { + Male = "male", + Female = "female", + Other = "other", + } + + testConvexToZod( + v.union(...Object.values(Gender).map(v.literal)), + z.literal([Gender.Male, Gender.Female, Gender.Other]), + ); + }); }); // Type equality helper: checks if two types are exactly equal (bidirectionally assignable) diff --git a/packages/convex-helpers/server/zod4.ts b/packages/convex-helpers/server/zod4.ts index ede9f539..61279289 100644 --- a/packages/convex-helpers/server/zod4.ts +++ b/packages/convex-helpers/server/zod4.ts @@ -650,7 +650,7 @@ export function convexToZod( break; case "array": { convexValidator satisfies VArray; - zodValidator = z.array(convexToZod(convexValidator.element)); + zodValidator = z.array(convexToZod(convexValidator.element as any)); break; } case "object": { @@ -1832,9 +1832,21 @@ export type ZodFromValidatorBase = }, ] > - : V extends VAny - ? z.ZodAny - : never; + : V extends VUnion< + any, + // VUnion<…, T[]> can + infer Is extends GenericValidator[], + OptionalProperty, + any + > + ? ZodValidatorFromConvex< + Is[number] + > extends zCore.$ZodType + ? ZodValidatorFromConvex + : never + : V extends VAny + ? z.ZodAny + : never; type BrandIfBranded = InnerType extends zCore.$brand From f66e54fe8b1fcb4830d58900185a2489cf6e0217 Mon Sep 17 00:00:00 2001 From: Nicolas Ettlin Date: Thu, 4 Dec 2025 14:19:20 -0800 Subject: [PATCH 2/2] Revert solution --- packages/convex-helpers/server/zod4.ts | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/packages/convex-helpers/server/zod4.ts b/packages/convex-helpers/server/zod4.ts index 61279289..ede9f539 100644 --- a/packages/convex-helpers/server/zod4.ts +++ b/packages/convex-helpers/server/zod4.ts @@ -650,7 +650,7 @@ export function convexToZod( break; case "array": { convexValidator satisfies VArray; - zodValidator = z.array(convexToZod(convexValidator.element as any)); + zodValidator = z.array(convexToZod(convexValidator.element)); break; } case "object": { @@ -1832,21 +1832,9 @@ export type ZodFromValidatorBase = }, ] > - : V extends VUnion< - any, - // VUnion<…, T[]> can - infer Is extends GenericValidator[], - OptionalProperty, - any - > - ? ZodValidatorFromConvex< - Is[number] - > extends zCore.$ZodType - ? ZodValidatorFromConvex - : never - : V extends VAny - ? z.ZodAny - : never; + : V extends VAny + ? z.ZodAny + : never; type BrandIfBranded = InnerType extends zCore.$brand