I'm working on a Zod validation schema for a product name input field that needs to be flexible in handling different validation requirements, such as minimum/maximum length and allowing special characters.
Here's a simplified version of the schema:
const { minLength = 3, maxLength = 50, allowSpecialCharacters = false, required = true } = options || {};
let schema = z.string({ required_error: `${displayName} is required` });
if (required) {
schema = schema.min(minLength, { message: `${displayName} must be at least ${minLength} characters long` });
}
schema = schema.max(maxLength, { message: `${displayName} must be less than ${maxLength} characters` });
if (!allowSpecialCharacters) {
schema = schema.refine((value) => /^[a-zA-Z0-9\s]*$/.test(value), {
message: `${displayName} must not contain special characters`
});
}
return schema.trim();
}
However, I'm encountering a TypeScript error when trying to reassign schema in the conditional logic:
Type 'ZodEffects<ZodString, string, string>' is missing the following properties from type 'ZodString': _regex, _addCheck, email, url, and 39 more.ts(2740)
let schema: z.ZodString
It seems like after applying refine, Zod returns a ZodEffects type, which leads to a type mismatch. How can I conditionally chain these refinements while maintaining type compatibility? Any insights would be appreciated!
Your running into this issue because, as you've seen, .refine()
changes the schema type to ZodEffects
. One thing to note about ZodEffects
is that it's a general schema type and doesn't have type specific methods like .min()
, .max()
, .email()
, etc. So even if we, for example, define schema: ZodString | ZodEffects<string,..>
, you'll get type issues trying to call the other utility methods. Here's an issue detailing the returned ZodEffects
, and the maintainer says in the thread that this will be changed in Zod 4.
But you're in luck. ZodString
has the method you need without using .refine()
, it's .regex()
, which will still return a ZodString
. So just change your special character check to this:
// ....
if (!allowSpecialCharacters) {
schema = schema.regex(/^[a-zA-Z0-9\s]*$/, {
message: `${displayName} must not contain special characters`
});
}