Description
I'm new to Zod, but I seem to be having an issue where something is breaking one of the schemas that I'm using for a discriminated union. The code below is causing an error in the final line (the error states that (type at position 1 of source isn't compatible with position 1 of target):
// Anchor event case
const anchorEventTimeRange = z
.object({
timeRange: z.literal("Anchor"),
yearsToInclude: z.array(z.string()),
anchorEvent: anchorEvent, // a base object with properties preDurationMonth and postDurationMonth, among others
trialPopulation: z.object({
displayName: z.string().min(1, { message: "Participant Population is required." }),
url: z.string(),
guid: z.string(),
}),
alterControlPostCosts: z.string().min(1, { message: "Cost-Capped Modification is required." }),
minimumEligibleDays: z.number().min(0, { message: "Must be a positive integer." }),
})
.superRefine((values, context) => {
const monthsPre = values.anchorEvent.preDurationMonth;
const monthsPost = values.anchorEvent.postDurationMonth;
const monthsPrePostMin = monthsPre < monthsPost ? monthsPre : monthsPost;
if (monthsPrePostMin > 0 && values.minimumEligibleDays > 30 * monthsPrePostMin) {
context.addIssue({
message: `Must be less than or equal to ${30 * monthsPrePostMin}`,
code: z.ZodIssueCode.custom,
path: ["minimumEligibleDays"],
});
}
});
// Year over year event case
const yearOverYearTimeRange = z.object({
timeRange: z.literal("YOY"),
anchorEvent: anchorEvent.optional(),
yearsToInclude: z
.array(z.string())
.length(2, { message: "Select 2 years to use for Year Over Year comparison." })
.refine(
function (val) {
const valNumeric = val.map(item => parseInt(item));
valNumeric.sort((a, b) => a - b);
return valNumeric[1] - valNumeric[0] === 1;
},
{ message: "Selected years must be consecutive." },
),
trialPopulation: z.object({
displayName: z.string().min(1, { message: "Participant Population is required." }),
url: z.string(),
guid: z.string(),
}),
isPopulationCompatible: z.boolean({ coerce: true }).nullable(),
alterControlPostCosts: z.string().nullable(),
minimumEligibleDays: z
.number({ message: "Must be a positive integer." })
.min(0, { message: "Must be a positive integer." })
.max(365, { message: "Must be less than or equal to 365." }),
});
const timeRangeUnion = z
.discriminatedUnion("timeRange", [yearOverYearTimeRange, anchorEventTimeRange]);
If I try to use this, then I get a red line under anchorEventTimeRange in the discriminated union. If I move the .superRefine from the anchorEventTimeRange definition to after the discriminated union definition (append it to the end of the final line), it will allow the union to form, but the validation errors aren't consistently firing the correct max value.
To give more info about what I'm doing, in the year over year case, the max value for minimumEligibleDays is 365. However, if it is an anchor event case, then max values of minimumEligibleDays is (30 * the lesser of anchorEvent.preDurationMonth or anchorEvent.postDurationMonth).
Any ideas about what is going wrong with this that causes the discriminated union objects to not be compatible? I've tried adding a .superRefine to both of objects for each case, but that causes the year over year case to have an error that it is missing a bunch of properties that are truncated, and if I add it to the anchor event case as shown above, then I get the error mentioned above.