Created
February 23, 2022 11:09
-
-
Save flofehrenbacher/71f93f7a423cfc11e1dd018325c241ec to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { z, ZodTypeAny, ZodUnion } from 'zod' | |
/** | |
* Zod helper for parsing arrays and ignore items not specified in the schema | |
* | |
* @param zodUnion - union of known types | |
* | |
* @example | |
* const binaryArraySchema = arrayIgnoreUnknown(z.union([z.literal('0'), z.literal('1')])) | |
* type BinaryArray = z.TypeOf<typeof binaryArraySchema> | |
* | |
* const binaryArray: BinaryArray = binaryArraySchema.parse(['0', '1', '2', '0']) | |
* console.log(binaryArray) // ['0', '1', '0'] | |
*/ | |
export function zodArrayIgnoreUnknown<T extends [ZodTypeAny, ...ZodTypeAny[]]>( | |
zodUnion: ZodUnion<T>, | |
) { | |
const isKnownItem = (item: unknown) => zodUnion.safeParse(item).success | |
return z.preprocess((val) => toSafeArray(val).filter(isKnownItem), z.array(zodUnion)) | |
} | |
function toSafeArray(item: unknown): Array<unknown> { | |
if (isArray(item)) { | |
return item | |
} | |
return [item] | |
} | |
function isArray<T>(item: unknown): item is Array<T> { | |
return Array.isArray(item) | |
} |
Thanks, the preprocess
solution is working with recursive schema.
function makeFilteredArraySchema<T extends ZodSchema>(schema: T) {
return z.preprocess((val) => {
const array = Array.isArray(val) ? val : [val]
return array.filter((item: unknown) => schema.safeParse(item).success)
}, z.array(schema))
}
Usage
const baseCategorySchema = z.object({
name: z.string(),
})
type Category = z.infer<typeof baseCategorySchema> & {
subcategories: Category[]
}
const categorySchema: z.ZodType<Category, z.ZodTypeDef, unknown> = baseCategorySchema.extend({
subcategories: z.lazy(() => makeFilteredArraySchema(categorySchema)),
})
categorySchema.parse({
name: 'People',
subcategories: [
{
name: 'Politicians',
subcategories: [
{
name: 'Presidents',
subcategories: [],
},
{
name: 123,
subcategories: [],
},
],
},
{
name: 456,
subcategories: [],
},
],
})
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@tintin10q If I remember correctly the ZodUnion requires at least two inputs. Because of that I used the same constraint