Skip to content

Commit

Permalink
Merge pull request #5909 from NomicFoundation/fix-zod-union-schema
Browse files Browse the repository at this point in the history
Fix the unionType zod helper
  • Loading branch information
alcuadrado authored Oct 30, 2024
2 parents d829b50 + 0d4a831 commit 5f5e626
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 4 deletions.
16 changes: 12 additions & 4 deletions v-next/hardhat-zod-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,19 @@ export const unionType = (
types: Parameters<typeof z.union>[0],
errorMessage: string,
) =>
// eslint-disable-next-line no-restricted-syntax -- This is the only place we allow z.union
z.union(types, {
errorMap: () => ({
// NOTE: The reason we use `z.any().superRefine` instead of `z.union` is that
// we found a bug with the `z.union` method that causes it to return a
// "deeper" validation error, when we expecte the `errorMessage`.
// See: https://github.com/colinhacks/zod/issues/2940#issuecomment-2380836931
z.any().superRefine((val, ctx) => {
if (types.some((t) => t.safeParse(val).success)) {
return;
}

ctx.addIssue({
code: z.ZodIssueCode.custom,
message: errorMessage,
}),
});
});

/**
Expand Down
34 changes: 34 additions & 0 deletions v-next/hardhat-zod-utils/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,40 @@ describe("unionType", () => {

assertParseResult(union.safeParse(undefined), "Expected error message");
});

it("Should work with deep errors", () => {
const mySchema = unionType(
[z.string(), z.number()],
"Expected a string or number",
);

assertParseResult(mySchema.safeParse(false), "Expected a string or number");

const mySchema2 = unionType(
[z.string().url(), z.number()],
"Expected a URL or number",
);

assertParseResult(mySchema2.safeParse(false), "Expected a URL or number");
assertParseResult(mySchema2.safeParse("a"), "Expected a URL or number");
});

it("Should accept valid data", () => {
const mySchema = unionType(
[z.string(), z.number()],
"Expected a string or number",
);

mySchema.parse("asd");
mySchema.parse(123);

const mySchema2 = unionType(
[z.string().url(), z.number()],
"Expected a URL or number",
);
mySchema2.parse("http://example.com");
mySchema2.parse(123);
});
});

describe("conditionalUnionType", () => {
Expand Down

0 comments on commit 5f5e626

Please sign in to comment.