Reputation: 156
I'm thinking about the following validation with zod and I have no clue on how to do it (or if it's possible with zod). I want an array of objects, all with the same shape, with some of them with literal props, I need these always present in the array.
Example: I need always in the array the objects those with name required1 and required2, and then other objects optionals following the same shape.
[
{
name: z.literal('required1'),
otherprop: z.number()
},
{
name: z.literal('required2'),
otherprop: z.number()
},
// I want to include one or more of the following too (optionals).
{
name: z.string(),
otherprop: z.number()
},
]
This other example needs to throw because required2 is missing
[
{
name: z.literal('required1'),
otherprop: z.number()
},
// I want to include one or more of the following too.
{
name: z.string(),
otherprop: z.number()
},
]
Any clue?
Upvotes: 3
Views: 24752
Reputation: 156
There is no way to solve the problem from just the typing system. I solved the issue using the refine
method from Zod. I will post two versions, one simpler with refine
and other more complex with superRefine
.
Base code
const elementSchema = z.object({
name: z.string(),
otherprop: z.number(),
})
type Element = z.infer<typeof elementSchema>;
// In my real code, here I have a function that returns the array of required names for the case.
const names = ['required1', 'required2'];
function refineNames(elements: Element[]): boolean {
return names.every((el: string) => elements.some(x => x.name === el));
}
Simple way using just refine
z.array(elementSchema).refine(
(elements) => refineNames(elements),
{ message: `There are missing names. Required names are ${names.join(', ')}`, }
);
Complex way using superRefine. But we can compare also for duplicate entries.
function hasDuplicates(elements: Element[]): boolean {
const names = elements.map(e => e.name);
return names.length !== new Set(names).size;
}
z.array(elementSchema).superRefine((elements, ctx) => {
if (refineNames(elements)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `There are missing names. Required names are ${names.join(', ')}`,
});
}
if (hasDuplicates(elements)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'No duplicated name allowed.',
});
}
}),
References:
Upvotes: 5