zod
zod copied to clipboard
feat: add option to skip formatting checks
I enjoy working with Zod a lot, as both a runtime type checker and a validation library. However, I've often found myself needing to confirm the shape of an object is correct from a type-safety perspective, without caring so much if an email field is an email.
Consider the following schema:
const personSchema = z.object({
id: z.string().uuid(),
name: z.string(),
email: z.string().email(),
age: z.number().positive().default(0),
})
If I were to use that to validate a simple form, I might supply the following as initial values:
{
id: '',
name: '',
email: '',
}
As far as I can see there's no way for me to check the Zod-inferred types at all for those initial values (including defaults), as a parse call would throw due to the email() check. But the property itself would still be a valid string.
I could solve this issue by maintaining two schemas: one that defines the data structure itself and one that adds additional validation on top. But this could be auto-generated from an external source, and maintaining two would just introduce unnecessary overhead.
Proposed Solution
This PR adds a new parameter to parse() which, when set to false, skips additional formatting checks on primitive types (number, string, date). This allows users to keep all the benefits of Zod's runtime type checking system when data may be correctly typed, but incorrectly formatted.
z.number().max(10).parse(100) // throws
z.number().max(10).parse(100, { validate: false }) // does not throw
z.number().max(10).parse('foobar', { validate: false }) // throws
Caveats
Naming
I'm a big fan of Zod's approach to naming. The team have clearly put a lot of thought into it. I'm really not sure what's the best/most idiomatic way to implement this change from a naming perspective and would appreciate any thoughts on approach!
Performance
This change fetches the context for every parse, regardless of whether anything has failed. I'm hoping somebody with better context will be able to flag if that's an issue, and if there's a better way of implementing this solution or passing the parse param around.
https://github.com/colinhacks/zod/pull/2741/files#diff-52632a4861fc9d7dc2dacef13cd91d60286dd706c1bb57438b8ee6a579a8796aR649-R655
ctx = this._getOrReturnCtx(input, ctx);
Deploy Preview for guileless-rolypoly-866f8a ready!
Built without sensitive environment variables
| Name | Link |
|---|---|
| Latest commit | a96c58966be3df2021ac824665a0b1834a27f292 |
| Latest deploy log | https://app.netlify.com/sites/guileless-rolypoly-866f8a/deploys/65ce10a067c2710008bada96 |
| Deploy Preview | https://deploy-preview-2741--guileless-rolypoly-866f8a.netlify.app |
| Preview on mobile | Toggle QR Code...Use your smartphone camera to open QR code link. |
To edit notification comments on pull requests, go to your Netlify site configuration.
Noting an equivalent PR has been released in Valibot for this, too 🙂
https://github.com/fabian-hiller/valibot/pull/164
I think it's an interesting idea. If if can be done in a performant way I'll try to incorporate something like this into Zod 4.
The parsing logic is going to be entirely rewritten in Zod 4 so I'm afraid merging this particular implementation doesn't make much sense. But I am still planning to investigate this for Zod 4.