8000
We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
There was an error while loading. Please reload this page.
-
Hi! Loving this library and promoting it. Using it in Nuxt 3, NestJS, everywhere.
This time, I have this:
z.object({ email: z.string().nonempty().email(), password: z.string() .min(6) .refine(v => { return /^(?=.*[A-Z]).+$/g.test(v) }, {message: 'At least one uppercase expected'}) .refine(v => { return /^(?=.*[0-9]).+$/g.test(v) }, {message: 'At least one digit expected'}) .refine(v => { return /^(?=.*[^a-zA-Z0-9]).+$/g.test(v) }, {message: 'At least one symbol expected'}) })
It works great!
But then I wanted to extend z with my own validators z.password() with the methods min and atLeastOne.
z
z.password()
min
atLeastOne
I kinda did it, but inside each method, I didn't know what to do, only than returning the object itself to create the chaining.
Tried defining a variable validations, and assigning to it in every method, but then what!?
validations
Unless each method is supposed to return a complex object, that contains the current validation, and all the methods for the chaining.
Can someone point me in the right direction? 😊💚🔥
Beta Was this translation helpful? Give feedback.
I think I'm very close... or not?
declare module 'zod' { type ZodPassword = { min(num: number): z.ZodPassword atLeastOne(kind: 'digit' | 'symbol' | 'uppercase'): z.ZodPassword & ReturnType<z.ZodString['refine']> nonempty(): z.ZodPassword } let password: () => ZodPassword } z.password = function (): z.ZodPassword { let result: any let validation: any = z.string() const obj: z.ZodPassword = { nonempty() { validation = validation.nonempty() return obj }, min(num: number) { validation = validation.min(num) return obj }, atLeastOne(kind: 'digit' | 'symbol' | 'uppercase') { let data: {message: string, regex: RegExp} = {regex: null, message: `At least one ${kind} expected`} if (kind === 'uppercase') { data.regex = /^(?=.*[A-Z]).+$/g } if (kind === 'digit') { data.regex = /^(?=.*[0-9]).+$/g } if (kind === 'symbol') { data.regex = /^(?=.*[^a-zA-Z0-9]).+$/g } let who if (!result) { who = validation } else { who = result } result = who.refine( (v) => { return data.regex.test(v) }, { message: data.message } ) return { ...obj, ...result } } } return obj }
I think I did it!!!???
But I would prefer not having to call the resolve method after all the constraints, to return the current value passed along the chaining.
resolve
declare module 'zod' { type ZodPassword = { min(num: number): z.ZodPassword atLeastOne(kind: 'digit' | 'symbol' | 'uppercase'): z.ZodPassword nonempty(): z.ZodPassword resolve(): z.ZodString } let password: () => ZodPassword } z.password = function (): z.ZodPassword { let validation: z.ZodString = z.string() const obj: z.ZodPassword = { resolve() { return validation }, nonempty() { validation = validation.nonempty() return obj }, min(num: number) { validation = validation.min(num) return obj }, atLeastOne(kind: 'digit' | 'symbol' | 'uppercase') { let data: { message: string; regex: RegExp } = { regex: null, message: `At least one ${kind} expected`, } if (kind === 'uppercase') { data.regex = /^(?=.*[A-Z]).+$/g } if (kind === 'digit') { data.regex = /^(?=.*[0-9]).+$/g } if (kind === 'symbol') { data.regex = /^(?=.*[^a-zA-Z0-9]).+$/g } validation = validation.regex(data.regex, data.message) return { ...obj, } }, } return obj } const RegisterSchema = z.object({ email: z.string().email().nonempty(), password: z .password() .nonempty() .min(6) .atLeastOne('digit') .atLeastOne('symbol') .atLeastOne('uppercase') .resolve() })
I just have a file like:
import { z as zod } from 'zod'; export const z = { ...zod, numericId: () => zod.coerce.number().int().safe(), };
and then import this file across my project.
If you need namespace support, if you will have to use something like:
/* eslint-disable @typescript-eslint/no-namespace */ // eslint-disable-next-line canonical/no-restricted-imports import * as zod from 'zod'; export namespace z { // @ts-expect-error TODO export * from 'zod'; export const numericId = () => zod.coerce.number().int().safe(); }
@colinhacks, is there a better alternative to this in v4? I also have similar things in a schema abstraction (mainly have a .decimal type that uses decimal.js and .date type that uses dayjs instead of js date). Those work well most of the time, but see challenges when I need to use libraries that integrate with zod, since they don't like my custom implementation. What are your thoughts about it in v4? Is there a better approach to this?
Thanks