I need to coerce string
formatted numbers to number
before using a value in z.discriminatedUnion
. Is this possible? Here is a simplified code snippet:
import { z } from "zod";
const BrushColorEnum = z.enum(
["BLUE_SILVER", "GREEN_SILVER", "RED", "GREEN_BLACK"],
{ message: "Invalid option" }
);
const BrushTypeEnum = z.enum(["THREAD", "MIXED", "CARLITE"], {
message: "Invalid option",
});
const noBrushSchema = z.object({
brush_qty: z.preprocess(
(val) => (typeof val === "string" ? parseInt(val, 10) : val),
z.literal(0)
),
brush_type: z
.union([
z.string().length(0, { message: "Invalid option" }),
z.undefined(),
z.literal(false),
])
.transform(() => undefined),
brush_color: z
.union([
z.string().length(0, { message: "Invalid option" }),
z.undefined(),
z.literal(false),
])
.transform(() => undefined),
});
const brushSchema = z.discriminatedUnion("brush_qty", [
noBrushSchema,
z.object({
brush_qty: z.literal("2"),
brush_type: BrushTypeEnum,
brush_color: BrushColorEnum,
}),
z.object({
brush_qty: z.literal("3"),
brush_type: BrushTypeEnum,
brush_color: BrushColorEnum,
}),
]);
console.log(brushSchema.safeParse({ brush_qty: "0" }).error); // message: "Invalid discriminator value. Expected 0 | '2' | '3'"
console.log(brushSchema.safeParse({ brush_qty: 0 })); // success
Take a look at the brush_qty
field. I would expect it to transform the string to number and I expect zod accept also the second safeParse
, but it doesn’t. It seems that the discriminator
get validated before passing on to the schema part.
If in this case it is impossible to coerce it to number
, what other choices than discriminatedUnion
I have to get more or less the same functionality?
Thanks in advance!