zod icon indicating copy to clipboard operation
zod copied to clipboard

feat: add option to skip formatting checks

Open jonlambert opened this issue 2 years ago • 3 comments
trafficstars

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);

jonlambert avatar Sep 15 '23 17:09 jonlambert

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...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

netlify[bot] avatar Sep 15 '23 17:09 netlify[bot]

Noting an equivalent PR has been released in Valibot for this, too 🙂

https://github.com/fabian-hiller/valibot/pull/164

jonlambert avatar Oct 05 '23 08:10 jonlambert

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.

colinhacks avatar Apr 17 '24 00:04 colinhacks

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.

colinhacks avatar Apr 30 '24 20:04 colinhacks