A way to exclude invalid items? #672
-
Hi! I am having trouble with solving an issue. const array = ['test', 'test2', 'test3', null]
// Wanted behavior
const validatedArray = z.array(z.string()).parse(array) // ['test', 'test2', 'test3'] With very complex types this would be very practical. Currently I am looping over each item in array and checking, which seems very complicated especially once you're nested deeply. Is there any better solution? |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 8 replies
-
I'm not aware of a way to adjust a Zod parser's behavior to get it to simply ignore items rather than throwing an error. You could write an array helper function and use If I was trying to implement a solution here, I would specify all of the types, or use const array = ['test', 'test2', 'test3', null]
const validatedArray = z.array(z.any())
.transform(
(as) => as.filter((a) => z.string().safeParse(a).success)
)
.parse(array) // ['test', 'test2', 'test3'] Maybe this is a useful generalization? const makeFilteredSchema = (s: z.ZodTypeAny) =>
z.array(z.any()).transform(
(as) => as.filter((a) => s.safeParse(a).success)
);
const filteredStringArraySchema = makeFilteredSchema(z.string());
const filteredStringOrNumberSchema = makeFilteredSchema(z.union([z.string(), z.number()]);
// More likely used inline:
const fooSchema = z.object({
bar: makeFilteredSchema(z.string()),
}); |
Beta Was this translation helpful? Give feedback.
-
Hello everybody, check out my |
Beta Was this translation helpful? Give feedback.
-
I made a couple of tweaks to @scotttrinh's answer const makeFilteredArraySchema = <T extends ZodSchema>(schema: T) =>
z
.array(z.unknown()) // `z.unknown()` more relevant than `z.any()` and avoid linting issue with any
.transform((items) =>
items?.filter(
(item): item is z.infer<T> => schema.safeParse(item).success
// `item is z.infer<T>` prevents the output type to be `unknown`. Ok to cast the type as it's been validated
)
);
const fooSchema = z.object({
bar: makeFilteredArraySchema(z.string()),
});
const fooType = z.infer<typeof fooSchema>
// fooType = { bar: string[] } |
Beta Was this translation helpful? Give feedback.
-
This might help. const zodArray = <S>(itemSchema: z.ZodType<S>) => {
const catchValue = {} as never;
return z
.array(itemSchema.catch(catchValue))
.transform((a) => a.filter((o) => o !== catchValue))
.catch([]);
}; Example const personSchema = z.object({
name: z.string(),
phones: zodArray(z.object({ label: z.string(), number: z.string() }))
});
const person = {
name: "John",
phones: [
{ number: "1234567890" }, // skipped
{ label: "Home" }, // skipped
{}, // skipped
{ label: "Mobile", number: "1234567891" }
]
}; Output {
"success": true,
"data": {
"name": "John",
"phones": [
{
"label": "Mobile",
"number": "1234567891"
}
]
}
} |
Beta Was this translation helpful? Give feedback.
-
this seems to be the most complete implementation I could muster up: const makeFilteredSchema = <S extends z.ZodTypeAny>(s: S) => {
return z.array(z.unknown()).transform((as) => {
const result: S[] = [];
for (const a of as) {
const parsed = s.safeParse(a);
if (parsed.success) {
result.push(parsed.data);
}
}
return result;
});
}; the reason why filter is not a great choice here, is because the schema itself could have additional transforms or maybe the data would have additional keys and in the case of filter they would not be removed, but because we're individually parsing each item, those additional transforms would be preserved. |
Beta Was this translation helpful? Give feedback.
I'm not aware of a way to adjust a Zod parser's behavior to get it to simply ignore items rather than throwing an error. You could write an array helper function and use
preprocess
maybe, but it's not going to be pretty sincepreprocess
starts atunknown
and requires you to do your own type narrowing.If I was trying to implement a solution here, I would specify all of the types, or use
z.array(z.any())
and use transform like this:Maybe this is a useful generalization?
CodeSandbox