- Published on
Zod: Set Min and Max After Transforming String to Number
- Authors
- Name
- Ripal & Zalak
Zod: Min and Max Validation After Transforming String to Number
You have a number or numeric string, and you want to:
- Transform the numeric string into a number.
- Apply
min
andmax
constraints on the resulting number.
Using a straightforward approach like chaining .min()
on a union schema doesn’t work because the methods are type-specific. Here’s a naive example:
const numberValid = z.number().or(z.string().regex(/\d+/).transform(Number))
const positiveNumber = numberValid.min(0) // Error: min() method doesn't exist
Solutions
z.coerce.number()
(Version 3.20+)
1. Using In newer versions of Zod, you can use z.coerce.number()
to achieve the desired behavior. This method ensures the input is coerced into a number, allowing you to apply .min()
and .max()
directly:
const numberValid = z.coerce.number().min(0)
console.log(numberValid.parse(123)) // OK
console.log(numberValid.parse('123')) // OK
console.log(numberValid.parse(-123)) // Error
console.log(numberValid.parse('-123')) // Error
refine
for Custom Validation
2. Using If you’re using an older version of Zod or need a different approach, you can use .refine()
to validate the range after transforming the value:
const numberValid = z
.number()
.or(z.string().regex(/\d+/).transform(Number))
.refine((n) => n >= 0, { message: 'Value must be >= 0' })
console.log(numberValid.parse('123')) // OK
console.log(numberValid.parse('-123')) // Error: Value must be >= 0
z.preprocess
for Pre-Validation Transformation
3. Using The z.preprocess
method lets you transform input data before applying a schema. This allows you to preprocess strings into numbers and then apply min
constraints:
const numberValid = z.preprocess((input) => {
if (typeof input === 'string' && /^\d+$/.test(input)) {
return Number(input)
}
return input
}, z.number().min(0))
console.log(numberValid.parse('123')) // OK
console.log(numberValid.parse(-123)) // Error: Number must be >= 0
console.log(numberValid.parse('abc')) // Error: Invalid input
pipe
for Chaining Transformations
4. Using You can use .pipe()
to chain transformations and validations sequentially:
const numberValid = z
.string()
.transform((val) => parseInt(val))
.pipe(z.number().min(0))
console.log(numberValid.parse('123')) // OK
console.log(numberValid.parse('-123')) // Error
Key Considerations
- Validation Order: Methods like
z.preprocess
ensure transformations occur before validation. - Error Messages: Customize error messages with the
message
property to provide better feedback. - Version Compatibility: Some features, like
z.coerce.number()
, are only available in newer Zod versions.
FAQs
Q1: What if my input is not guaranteed to be a numeric string?
Use a union schema with conditional transformations, or preprocess the input to handle edge cases.
Q2: How do I handle custom error messages?
You can pass an object with a message
property to methods like .refine()
or .min()
:
z.number().min(0, { message: 'Value must be zero or greater' })
Q3: Can I use these techniques in React or Node.js projects?
Yes, Zod is compatible with both environments and works seamlessly for validation in APIs, forms, and more.