zod 3.22.4 → 3.23.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,6 +37,8 @@
37
37
  <br/>
38
38
  <br/>
39
39
 
40
+ > Zod 3.23 is out! View the [release notes](https://github.com/colinhacks/zod/releases/tag/v3.23.0).
41
+
40
42
  > These docs have been translated into [Chinese](./README_ZH.md).
41
43
 
42
44
  ## Table of contents
@@ -51,6 +53,7 @@
51
53
  - [Gold](#gold)
52
54
  - [Silver](#silver)
53
55
  - [Bronze](#bronze)
56
+ - [Copper](#copper)
54
57
  - [Ecosystem](#ecosystem)
55
58
  - [Resources](#resources)
56
59
  - [API libraries](#api-libraries)
@@ -69,7 +72,9 @@
69
72
  - [Coercion for primitives](#coercion-for-primitives)
70
73
  - [Literals](#literals)
71
74
  - [Strings](#strings)
72
- - [ISO datetimes](#iso-datetimes)
75
+ - [Datetimes](#datetimes)
76
+ - [Dates](#dates)
77
+ - [Times](#times)
73
78
  - [IP addresses](#ip-addresses)
74
79
  - [Numbers](#numbers)
75
80
  - [BigInts](#bigints)
@@ -180,145 +185,132 @@ Some other great aspects:
180
185
 
181
186
  Sponsorship at any level is appreciated and encouraged. For individual developers, consider the [Cup of Coffee tier](https://github.com/sponsors/colinhacks). If you built a paid product using Zod, consider one of the [podium tiers](https://github.com/sponsors/colinhacks).
182
187
 
183
- #### Gold
188
+ #### Platinum
184
189
 
185
- <table>
190
+ > [Email me](mailto:colin@colinhacks.com) to discuss sponsoring Zod at this level.
191
+
192
+ <!-- <table>
186
193
  <tr>
187
194
  <td align="center">
188
- <a href="https://speakeasyapi.dev/">
189
- <img src="https://avatars.githubusercontent.com/u/91446104?s=200&v=4" width="200px;" alt="Speakeasy API" />
195
+ <a href="https://www.example.com" target="_blank">
196
+ <img src="https://example.com/image.png" height="100px;" alt="XXX" />
190
197
  </a>
191
198
  <br />
192
- <b>Speakeasy</b>
193
- <br />
194
- <a href="https://speakeasyapi.dev/">speakeasyapi.dev</a>
199
+ <b>XXX</b>
195
200
  <br />
196
- <p width="200px">SDKs, Terraform, Docs.<br/>Your API made enterprise-ready.</p>
201
+ <a href="https://www.example.com" target="_blank">example.com</a>
197
202
  </td>
203
+ </tr>
204
+ </table> -->
205
+
206
+ #### Gold
207
+
208
+ <table>
209
+ <tr>
198
210
  <td align="center">
199
- <a href="https://glow.app/">
200
- <img src="https://i.imgur.com/R0R43S2.jpg" width="200px;" alt="Glow Wallet" />
201
- </a>
211
+ <img src="https://avatars.githubusercontent.com/u/80861386?s=200&v=4" height="45px;" alt="Cerbos" />
202
212
  <br />
203
- <b>Glow Wallet</b>
213
+ <a href="https://cerbos.dev/" target="_blank">Cerbos</a>
214
+ </td>
215
+ <td align="center">
216
+ <img src="https://avatars.githubusercontent.com/u/301879?s=200&v=4" height="45px;" alt="Scalar.com logo" />
204
217
  <br />
205
- <a href="https://glow.app/">glow.app</a>
218
+ <a href="https://scalar.com/" target="_blank">Scalar</a>
219
+ </td>
220
+ <td align="center">
221
+ <img src="https://avatars.githubusercontent.com/u/91446104?s=200&v=4" height="45px;" alt="Speakeasy API" />
206
222
  <br />
207
- <p width="200px">Your new favorite
208
- <br/>
209
- Solana wallet.</p>
223
+ <a href="https://speakeasyapi.dev/" target="_blank">Speakeasy</a>
210
224
  </td>
211
- </tr>
212
- <tr>
213
225
  <td align="center">
214
- <a href="https://deletype.com/">
215
- <img src="https://avatars0.githubusercontent.com/u/15068039?s=200&v=4" width="200px;" alt="Deletype logo" />
216
- </a>
226
+ <img src="https://avatars0.githubusercontent.com/u/15068039?s=200&v=4" height="45px;" alt="Deletype logo" />
217
227
  <br />
218
- <b>Deletype</b>
228
+ <a href="https://deletype.com" target="_blank">Deletype</a>
229
+ </td>
230
+ <td align="center">
231
+ <img src="https://avatars.githubusercontent.com/u/95297378?s=200&v=4" height="45px;" alt="Trigger.dev logo" />
219
232
  <br />
220
- <a href="https://deletype.com">deletype.com</a>
233
+ <a href="https://trigger.dev" target="_blank">Trigger.dev</a>
221
234
  </td>
235
+ </tr><tr>
222
236
  <td align="center">
223
- <a href="https://trigger.dev/">
224
- <img src="https://avatars.githubusercontent.com/u/95297378?s=200&v=4" width="200px;" alt="Trigger.dev logo" />
225
- </a>
237
+ <img src="https://avatars.githubusercontent.com/u/125754?s=200&v=4" height="45px;" alt="Transloadit logo" />
226
238
  <br />
227
- <b>Trigger.dev</b>
239
+ <a href="https://transloadit.com/?utm_source=zod&utm_medium=refe
240
+ rral&utm_campaign=sponsorship&utm_content=github" target="_blank">Transloadit</a>
241
+ </td>
242
+ <td align="center">
243
+ <img src="https://avatars.githubusercontent.com/u/107880645?s=200&v=4" height="45px;" alt="Infisical logo" />
228
244
  <br />
229
- <a href="https://trigger.dev">trigger.dev</a>
230
- <br/>
231
- <p>Effortless automation for developers.</p>
245
+ <a href="https://infisical.com" target="_blank">Infisical</a>
232
246
  </td>
233
- </tr>
234
- <tr>
235
247
  <td align="center">
236
- <a href="https://transloadit.com/?utm_source=zod&utm_medium=referral&utm_campaign=sponsorship&utm_content=github">
237
- <img src="https://avatars.githubusercontent.com/u/125754?s=200&v=4" width="200px;" alt="Transloadit logo" />
238
- </a>
248
+ <img src="https://avatars.githubusercontent.com/u/91036480?s=200&v=4" height="45px;" alt="Whop logo" />
239
249
  <br />
240
- <b>Transloadit</b>
250
+ <a href="https://whop.com/" target="_blank">Whop</a>
251
+ </td>
252
+ <td align="center">
253
+ <img src="https://avatars.githubusercontent.com/u/36402888?s=200&v=4" height="45px;" alt="CryptoJobsList logo" />
241
254
  <br />
242
- <a href="https://transloadit.com/?utm_source=zod&utm_medium=referral&utm_campaign=sponsorship&utm_content=github">transloadit.com</a>
243
- <br/>
244
- <p>Simple file processing for developers.</p>
255
+ <a href="https://cryptojobslist.com/" target="_blank">CryptoJobsList</a>
245
256
  </td>
246
257
  <td align="center">
247
- <a href="https://infisical.com">
248
- <img src="https://avatars.githubusercontent.com/u/107880645?s=200&v=4" width="200px;" alt="Infisical logo" />
249
- </a>
258
+ <img src="https://avatars.githubusercontent.com/u/70170949?s=200&v=4" height="45px;" alt="Plain logo" />
250
259
  <br />
251
- <b>Infisical</b>
260
+ <a href="https://plain.com/" target="_blank">Plain.</a>
261
+ </td>
262
+ </tr><tr>
263
+ <td align="center">
264
+ <img src="https://avatars.githubusercontent.com/u/78935958?s=200&v=4" height="45px;" alt="Inngest logo" />
252
265
  <br />
253
- <a href="https://infisical.com">infisical.com</a>
254
- <br/>
255
- <p>Open-source platform for secret<br/>management: sync secrets across your<br/>team/infrastructure and prevent secret leaks.</p>
266
+ <a href="https://inngest.com/" target="_blank">Inngest</a>
256
267
  </td>
257
- </tr>
258
- <tr>
259
268
  <td align="center">
260
- <a href="https://whop.com/">
261
- <img src="https://avatars.githubusercontent.com/u/91036480?s=200&v=4" width="200px;" alt="Whop logo" />
262
- </a>
269
+ <img src="https://avatars.githubusercontent.com/u/13880908?s=200&v=4" height="45px;" alt="Storyblok CMS" />
263
270
  <br />
264
- <b>Whop</b>
271
+ <a href="https://storyblok.com/" target="_blank">Storyblok</a>
272
+ </td>
273
+ <td align="center">
274
+ <img src="https://avatars.githubusercontent.com/u/16199997?s=200&v=4" height="45px;" alt="Mux logo" />
265
275
  <br />
266
- <a href="https://whop.com/">whop.com</a>
276
+ <a href="https://mux.link/zod" target="_blank">Mux</a>
277
+ </td>
278
+ <td align="center">
279
+ <img src="https://avatars.githubusercontent.com/u/180984?v=4" height="45px;" alt="@emreb" />
267
280
  <br />
268
- <p width="200px">A marketplace for really cool internet products.</p>
281
+ <a href="https://github.com/emreb" target="_blank"><code>@emreb</code></a>
269
282
  </td>
283
+ </tr>
270
284
  </table>
271
285
 
272
286
  #### Silver
273
287
 
274
288
  <table>
275
289
  <tr>
276
- <td align="center" colspan="2">
290
+ <td align="center">
277
291
  <a href="https://www.numeric.io">
278
- <img src="https://i.imgur.com/kTiLtZt.png" width="250px;" alt="Numeric logo" />
292
+ <img src="https://i.imgur.com/kTiLtZt.png" height="40px;" alt="Numeric logo" />
279
293
  </a>
280
- <br />
281
- <b>Numeric</b>
282
- <br />
283
- <a href="https://www.numeric.io">numeric.io</a>
284
294
  </td>
285
- <td align="center">
286
- <a href="https://marcatopartners.com/">
287
- <img src="https://avatars.githubusercontent.com/u/84106192?s=200&v=4" width="150px;" alt="Marcato Partners" />
295
+ <td>
296
+ <a href="https://marcatopartners.com">
297
+ <img src="https://avatars.githubusercontent.com/u/84106192?s=200&v=4" height="40px;" alt="Marcato Partners" />
288
298
  </a>
289
- <br />
290
- <b>Marcato Partners</b>
291
- <br />
292
- <a href="https://marcatopartners.com/">marcatopartners.com</a>
293
299
  </td>
294
- </tr>
295
- <tr>
296
- <td align="center">
300
+ <td>
297
301
  <a href="https://interval.com">
298
- <img src="https://avatars.githubusercontent.com/u/67802063?s=200&v=4" width="150px;" alt="" />
302
+ <img src="https://avatars.githubusercontent.com/u/67802063?s=200&v=4" height="40px;" alt="" />
299
303
  </a>
300
- <br />
301
- <b>Interval</b>
302
- <br />
303
- <a href="https://interval.com">interval.com</a>
304
304
  </td>
305
- <td align="center">
305
+ <td>
306
306
  <a href="https://seasoned.cc">
307
- <img src="https://avatars.githubusercontent.com/u/33913103?s=200&v=4" width="150px;" alt="" />
307
+ <img src="https://avatars.githubusercontent.com/u/33913103?s=200&v=4" height="40px;" alt="" />
308
308
  </a>
309
- <br />
310
- <b>Seasoned Software</b>
311
- <br />
312
- <a href="https://seasoned.cc">seasoned.cc</a>
313
309
  </td>
314
- <td align="center">
310
+ <td>
315
311
  <a href="https://www.bamboocreative.nz/">
316
- <img src="https://avatars.githubusercontent.com/u/41406870?v=4" width="150px;" alt="Bamboo Creative logo" />
312
+ <img src="https://avatars.githubusercontent.com/u/41406870?v=4" height="40px;" alt="Bamboo Creative logo" />
317
313
  </a>
318
- <br />
319
- <b>Bamboo Creative</b>
320
- <br />
321
- <a href="https://www.bamboocreative.nz">bamboocreative.nz</a>
322
314
  </td>
323
315
  </tr>
324
316
  </table>
@@ -327,101 +319,31 @@ Sponsorship at any level is appreciated and encouraged. For individual developer
327
319
 
328
320
  <table>
329
321
  <tr>
330
- <td align="center">
331
- <a href="https://twitter.com/flybayer">
332
- <img src="https://avatars2.githubusercontent.com/u/8813276?s=460&u=4ff8beb9a67b173015c4b426a92d89cab960af1b&v=4" width="100px;" alt=""/>
333
- </a>
334
- <br />
335
- <b>Brandon Bayer</b>
336
- <br/>
337
- <a href="https://twitter.com/flybayer">@flybayer</a>,
338
- <span>creator of <a href="https://blitzjs.com">Blitz.js</a></span>
339
- <br />
340
- </td>
341
- <td align="center">
342
- <a href="https://github.com/brabeji">
343
- <img src="https://avatars.githubusercontent.com/u/2237954?v=4" width="100px;" alt=""/>
344
- </a>
345
- <br />
346
- <b>Jiří Brabec</b>
347
- <br/>
348
- <a href="https://github.com/brabeji">@brabeji</a>
349
- <br />
350
- </td>
351
- <td align="center">
352
- <a href="https://twitter.com/alexdotjs">
353
- <img src="https://avatars.githubusercontent.com/u/459267?v=4" width="100px;" alt="" />
354
- </a>
355
- <br />
356
- <b>Alex Johansson</b>
357
- <br />
358
- <a href="https://twitter.com/alexdotjs">@alexdotjs</a>
359
- </td>
322
+ <td>Brandon Bayer</td>
323
+ <td>Jiří Brabec</td>
324
+ <td>Alex Johansson</td>
325
+ <td>Fungible Systems</td>
360
326
  </tr>
361
327
  <tr>
362
- <td align="center">
363
- <a href="https://fungible.systems/">
364
- <img src="https://avatars.githubusercontent.com/u/80220121?s=200&v=4" width="100px;" alt="Fungible Systems logo"/>
365
- </a>
366
- <br />
367
- <b>Fungible Systems</b>
368
- <br/>
369
- <a href="https://fungible.systems/">fungible.systems</a>
370
- <br />
371
- </td>
372
- <td align="center">
373
- <a href="https://adaptable.io/">
374
- <img src="https://avatars.githubusercontent.com/u/60378268?s=200&v=4" width="100px;" alt=""/>
375
- </a>
376
- <br />
377
- <b>Adaptable</b>
378
- <br/>
379
- <a href="https://adaptable.io/">adaptable.io</a>
380
- <br />
381
- </td>
382
- <td align="center">
383
- <a href="https://www.avanawallet.com/">
384
- <img src="https://avatars.githubusercontent.com/u/105452197?s=200&v=4" width="100px;" alt="Avana Wallet logo"/>
385
- </a>
386
- <br />
387
- <b>Avana Wallet</b>
388
- <br/>
389
- <a href="https://www.avanawallet.com/">avanawallet.com</a><br/>
390
- <span>Solana non-custodial wallet</span>
391
- <br />
392
- </td>
328
+ <td>Adaptable</td>
329
+ <td>Avana Wallet</td>
330
+ <td>Jason Lengstorf</td>
331
+ <td>Global Illumination, Inc.</td>
393
332
  </tr>
394
333
  <tr>
395
- <td align="center">
396
- <a href="https://learnwithjason.dev">
397
- <img src="https://avatars.githubusercontent.com/u/66575486?s=200&v=4" width="100px;" alt="Learn with Jason logo"/>
398
- </a>
399
- <br />
400
- <b>Jason Lengstorf</b>
401
- <br/>
402
- <a href="https://learnwithjason.dev/">learnwithjason.dev</a>
403
- <br />
404
- </td>
405
- <td align="center">
406
- <a href="https://ill.inc/">
407
- <img src="https://avatars.githubusercontent.com/u/89107581?s=200&v=4" width="100px;" alt="Global Illumination"/>
408
- </a>
409
- <br />
410
- <b>Global Illumination, Inc.</b>
411
- <br/>
412
- <a href="https://ill.inc/">ill.inc</a>
413
- <br />
414
- </td>
415
- <td align="center">
416
- <a href="https://www.masterborn.com/career?utm_source=github&utm_medium=referral&utm_campaign=zodsponsoring">
417
- <img src="https://avatars.githubusercontent.com/u/48984031?s=200&v=4" width="100px;" alt="MasterBorn logo"/>
418
- </a>
419
- <br />
420
- <b>MasterBorn</b>
421
- <br/>
422
- <a href="https://www.masterborn.com/career?utm_source=github&utm_medium=referral&utm_campaign=zodsponsoring">masterborn.com</a>
423
- <br />
424
- </td>
334
+ <td>MasterBorn</td>
335
+ <td>Ryan Palmer</td>
336
+ <td>Michael Sweeney</td>
337
+ <td>Nextbase</td>
338
+ </tr>
339
+ <tr>
340
+ <td>Remotion</td>
341
+ <td>Connor Sinnott</td>
342
+ <td>Mohammad-Ali A'râbi</td>
343
+ <td>Supatool</td>
344
+ </tr>
345
+ <tr>
346
+ <td>Social Crow</td>
425
347
  </tr>
426
348
  </table>
427
349
 
@@ -439,6 +361,7 @@ There are a growing number of tools that are built atop or support Zod natively!
439
361
  - [`tRPC`](https://github.com/trpc/trpc): Build end-to-end typesafe APIs without GraphQL.
440
362
  - [`@anatine/zod-nestjs`](https://github.com/anatine/zod-plugins/tree/main/packages/zod-nestjs): Helper methods for using Zod in a NestJS project.
441
363
  - [`zod-endpoints`](https://github.com/flock-community/zod-endpoints): Contract-first strictly typed endpoints with Zod. OpenAPI compatible.
364
+ - [`zhttp`](https://github.com/evertdespiegeleer/zhttp): An OpenAPI compatible, strictly typed http library with Zod input and response validation.
442
365
  - [`domain-functions`](https://github.com/SeasonedSoftware/domain-functions/): Decouple your business logic from your framework using composable functions. With first-class type inference from end to end powered by Zod schemas.
443
366
  - [`@zodios/core`](https://github.com/ecyrbe/zodios): A typescript API client with runtime and compile time validation backed by axios and zod.
444
367
  - [`express-zod-api`](https://github.com/RobinTail/express-zod-api): Build Express-based APIs with I/O schema validation and custom middlewares.
@@ -447,12 +370,12 @@ There are a growing number of tools that are built atop or support Zod natively!
447
370
 
448
371
  #### Form integrations
449
372
 
450
- - [`conform`](https://conform.guide/api/zod): A progressive enhancement first form validation library for Remix and React Router
451
373
  - [`react-hook-form`](https://github.com/react-hook-form/resolvers#zod): A first-party Zod resolver for React Hook Form.
452
374
  - [`zod-validation-error`](https://github.com/causaly/zod-validation-error): Generate user-friendly error messages from `ZodError`s.
453
375
  - [`zod-formik-adapter`](https://github.com/robertLichtnow/zod-formik-adapter): A community-maintained Formik adapter for Zod.
454
376
  - [`react-zorm`](https://github.com/esamattis/react-zorm): Standalone `<form>` generation and validation for React using Zod.
455
377
  - [`zodix`](https://github.com/rileytomasek/zodix): Zod utilities for FormData and URLSearchParams in Remix loaders and actions.
378
+ - [`conform`](https://conform.guide/api/zod/parseWithZod): A typesafe form validation library for progressive enhancement of HTML forms. Works with Remix and Next.js.
456
379
  - [`remix-params-helper`](https://github.com/kiliman/remix-params-helper): Simplify integration of Zod with standard URLSearchParams and FormData for Remix apps.
457
380
  - [`formik-validator-zod`](https://github.com/glazy/formik-validator-zod): Formik-compliant validator library that simplifies using Zod with Formik.
458
381
  - [`zod-i18n-map`](https://github.com/aiji42/zod-i18n): Useful for translating Zod error messages.
@@ -490,6 +413,8 @@ There are a growing number of tools that are built atop or support Zod natively!
490
413
  - [`zod-prisma-types`](https://github.com/chrishoermann/zod-prisma-types) Create Zod types from your Prisma models.
491
414
  - [`quicktype`](https://app.quicktype.io/): Convert JSON objects and JSON schemas into Zod schemas.
492
415
  - [`@sanity-typed/zod`](https://github.com/saiichihashimoto/sanity-typed/tree/main/packages/zod): Generate Zod Schemas from [Sanity Schemas](https://www.sanity.io/docs/schema-types).
416
+ - [`java-to-zod`](https://github.com/ivangreene/java-to-zod): Convert POJOs to Zod schemas
417
+ - [`Orval`](https://github.com/anymaniax/orval): Generate Zod schemas from OpenAPI schemas
493
418
 
494
419
  #### Mocking
495
420
 
@@ -504,13 +429,18 @@ There are a growing number of tools that are built atop or support Zod natively!
504
429
  - [`freerstore`](https://github.com/JacobWeisenburger/freerstore): Firestore cost optimizer.
505
430
  - [`slonik`](https://github.com/gajus/slonik/tree/gajus/add-zod-validation-backwards-compatible#runtime-validation-and-static-type-inference): Node.js Postgres client with strong Zod integration.
506
431
  - [`soly`](https://github.com/mdbetancourt/soly): Create CLI applications with zod.
432
+ - [`pastel`](https://github.com/vadimdemedes/pastel): Create CLI applications with react, zod, and ink.
507
433
  - [`zod-xlsx`](https://github.com/sidwebworks/zod-xlsx): A xlsx based resource validator using Zod schemas.
508
434
  - [`znv`](https://github.com/lostfictions/znv): Type-safe environment parsing and validation for Node.js with Zod schemas.
435
+ - [`zod-config`](https://github.com/alexmarqs/zod-config): Load configurations across multiple sources with flexible adapters, ensuring type safety with Zod.
509
436
 
510
437
  #### Utilities for Zod
511
438
 
512
439
  - [`zod_utilz`](https://github.com/JacobWeisenburger/zod_utilz): Framework agnostic utilities for Zod.
440
+ - [`zod-playground`](https://github.com/marilari88/zod-playground): A tool for learning and testing Zod schema validation functionalities. [Link](https://zod-playground.vercel.app/).
513
441
  - [`zod-sandbox`](https://github.com/nereumelo/zod-sandbox): Controlled environment for testing zod schemas. [Live demo](https://zod-sandbox.vercel.app/).
442
+ - [`zod-dev`](https://github.com/schalkventer/zod-dev): Conditionally disables Zod runtime parsing in production.
443
+ - [`zod-accelerator`](https://github.com/duplojs/duplojs-zod-accelerator): Accelerates Zod's throughput up to ~100x.
514
444
 
515
445
  ## Installation
516
446
 
@@ -635,16 +565,26 @@ Zod now provides a more convenient way to coerce primitive values.
635
565
  const schema = z.coerce.string();
636
566
  schema.parse("tuna"); // => "tuna"
637
567
  schema.parse(12); // => "12"
568
+ ```
569
+
570
+ During the parsing step, the input is passed through the `String()` function, which is a JavaScript built-in for coercing data into strings.
571
+
572
+ ```ts
573
+ schema.parse(12); // => "12"
638
574
  schema.parse(true); // => "true"
575
+ schema.parse(undefined); // => "undefined"
576
+ schema.parse(null); // => "null"
639
577
  ```
640
578
 
641
- During the parsing step, the input is passed through the `String()` function, which is a JavaScript built-in for coercing data into strings. Note that the returned schema is a `ZodString` instance so you can use all string methods.
579
+ The returned schema is a normal `ZodString` instance so you can use all string methods.
642
580
 
643
581
  ```ts
644
582
  z.coerce.string().email().min(5);
645
583
  ```
646
584
 
647
- All primitive types support coercion.
585
+ **How coercion works**
586
+
587
+ All primitive types support coercion. Zod coerces all inputs using the built-in constructors: `String(input)`, `Number(input)`, `new Date(input)`, etc.
648
588
 
649
589
  ```ts
650
590
  z.coerce.string(); // String(input)
@@ -654,22 +594,25 @@ z.coerce.bigint(); // BigInt(input)
654
594
  z.coerce.date(); // new Date(input)
655
595
  ```
656
596
 
657
- **Boolean coercion**
658
-
659
- Zod's boolean coercion is very simple! It passes the value into the `Boolean(value)` function, that's it. Any truthy value will resolve to `true`, any falsy value will resolve to `false`.
597
+ **Note** — Boolean coercion with `z.coerce.boolean()` may not work how you expect. Any [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) value is coerced to `true`, and any [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) value is coerced to `false`.
660
598
 
661
599
  ```ts
662
- z.coerce.boolean().parse("tuna"); // => true
663
- z.coerce.boolean().parse("true"); // => true
664
- z.coerce.boolean().parse("false"); // => true
665
- z.coerce.boolean().parse(1); // => true
666
- z.coerce.boolean().parse([]); // => true
600
+ const schema = z.coerce.boolean(); // Boolean(input)
667
601
 
668
- z.coerce.boolean().parse(0); // => false
669
- z.coerce.boolean().parse(undefined); // => false
670
- z.coerce.boolean().parse(null); // => false
602
+ schema.parse("tuna"); // => true
603
+ schema.parse("true"); // => true
604
+ schema.parse("false"); // => true
605
+ schema.parse(1); // => true
606
+ schema.parse([]); // => true
607
+
608
+ schema.parse(0); // => false
609
+ schema.parse(""); // => false
610
+ schema.parse(undefined); // => false
611
+ schema.parse(null); // => false
671
612
  ```
672
613
 
614
+ For more control over coercion logic, consider using [`z.preprocess`](#preprocess) or [`z.pipe()`](#pipe).
615
+
673
616
  ## Literals
674
617
 
675
618
  Literal schemas represent a [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types), like `"hello world"` or `5`.
@@ -702,6 +645,7 @@ z.string().email();
702
645
  z.string().url();
703
646
  z.string().emoji();
704
647
  z.string().uuid();
648
+ z.string().nanoid();
705
649
  z.string().cuid();
706
650
  z.string().cuid2();
707
651
  z.string().ulid();
@@ -709,13 +653,19 @@ z.string().regex(regex);
709
653
  z.string().includes(string);
710
654
  z.string().startsWith(string);
711
655
  z.string().endsWith(string);
712
- z.string().datetime(); // ISO 8601; default is without UTC offset, see below for options
713
- z.string().ip(); // defaults to IPv4 and IPv6, see below for options
656
+ z.string().datetime(); // ISO 8601; by default only `Z` timezone allowed
657
+ z.string().ip(); // defaults to allow both IPv4 and IPv6
714
658
 
715
- // transformations
659
+ // transforms
716
660
  z.string().trim(); // trim whitespace
717
661
  z.string().toLowerCase(); // toLowerCase
718
662
  z.string().toUpperCase(); // toUpperCase
663
+
664
+ // added in Zod 3.23
665
+ z.string().date(); // ISO date format (YYYY-MM-DD)
666
+ z.string().time(); // ISO time format (HH:mm:ss[.SSSSSS])
667
+ z.string().duration(); // ISO 8601 duration
668
+ z.string().base64();
719
669
  ```
720
670
 
721
671
  > Check out [validator.js](https://github.com/validatorjs/validator.js) for a bunch of other useful string validation functions that can be used in conjunction with [Refinements](#refine).
@@ -743,10 +693,14 @@ z.string().includes("tuna", { message: "Must include tuna" });
743
693
  z.string().startsWith("https://", { message: "Must provide secure URL" });
744
694
  z.string().endsWith(".com", { message: "Only .com domains allowed" });
745
695
  z.string().datetime({ message: "Invalid datetime string! Must be UTC." });
696
+ z.string().date({ message: "Invalid date string!" });
697
+ z.string().time({ message: "Invalid time string!" });
746
698
  z.string().ip({ message: "Invalid IP address" });
747
699
  ```
748
700
 
749
- ### ISO datetimes
701
+ ### Datetimes
702
+
703
+ As you may have noticed, Zod string includes a few date/time related validations. These validations are regular expression based, so they are not as strict as a full date/time library. However, they are very convenient for validating user input.
750
704
 
751
705
  The `z.string().datetime()` method enforces ISO 8601; default is no timezone offsets and arbitrary sub-second decimal precision.
752
706
 
@@ -781,6 +735,47 @@ datetime.parse("2020-01-01T00:00:00Z"); // fail
781
735
  datetime.parse("2020-01-01T00:00:00.123456Z"); // fail
782
736
  ```
783
737
 
738
+ ### Dates
739
+
740
+ > Added in Zod 3.23
741
+
742
+ The `z.string().date()` method validates strings in the format `YYYY-MM-DD`.
743
+
744
+ ```ts
745
+ const date = z.string().date();
746
+
747
+ date.parse("2020-01-01"); // pass
748
+ date.parse("2020-1-1"); // fail
749
+ date.parse("2020-01-32"); // fail
750
+ ```
751
+
752
+ ### Times
753
+
754
+ > Added in Zod 3.23
755
+
756
+ The `z.string().time()` method validates strings in the format `HH:MM:SS[.s+]`. The second can include arbitrary decimal precision. It does not allow timezone offsets of any kind.
757
+
758
+ ```ts
759
+ const time = z.string().time();
760
+
761
+ time.parse("00:00:00"); // pass
762
+ time.parse("09:52:31"); // pass
763
+ time.parse("23:59:59.9999999"); // pass (arbitrary precision)
764
+
765
+ time.parse("00:00:00.123Z"); // fail (no `Z` allowed)
766
+ time.parse("00:00:00.123+02:00"); // fail (no offsets allowed)
767
+ ```
768
+
769
+ You can set the `precision` option to constrain the allowable decimal precision.
770
+
771
+ ```ts
772
+ const time = z.string().time({ precision: 3 });
773
+
774
+ time.parse("00:00:00.123"); // pass
775
+ time.parse("00:00:00.123456"); // fail
776
+ time.parse("00:00:00"); // fail
777
+ ```
778
+
784
779
  ### IP addresses
785
780
 
786
781
  The `z.string().ip()` method by default validate IPv4 and IPv6.
@@ -869,7 +864,7 @@ You can customize certain error messages when creating a nan schema.
869
864
  ```ts
870
865
  const isNaN = z.nan({
871
866
  required_error: "isNaN is required",
872
- invalid_type_error: "isNaN must be not a number",
867
+ invalid_type_error: "isNaN must be 'not a number'",
873
868
  });
874
869
  ```
875
870
 
@@ -953,7 +948,7 @@ const fish = ["Salmon", "Tuna", "Trout"];
953
948
  const FishEnum = z.enum(fish);
954
949
  ```
955
950
 
956
- **Autocompletion**
951
+ **`.enum`**
957
952
 
958
953
  To get autocompletion with a Zod enum, use the `.enum` property of your schema:
959
954
 
@@ -976,6 +971,16 @@ You can also retrieve the list of options as a tuple with the `.options` propert
976
971
  FishEnum.options; // ["Salmon", "Tuna", "Trout"];
977
972
  ```
978
973
 
974
+ **`.exclude/.extract()`**
975
+
976
+ You can create subsets of a Zod enum with the `.exclude` and `.extract` methods.
977
+
978
+ ```ts
979
+ const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]);
980
+ const SalmonAndTrout = FishEnum.extract(["Salmon", "Trout"]);
981
+ const TunaOnly = FishEnum.exclude(["Salmon", "Trout"]);
982
+ ```
983
+
979
984
  ## Native enums
980
985
 
981
986
  Zod enums are the recommended approach to defining and validating enums. But if you need to validate against an enum from a third-party library (or you don't want to rewrite your existing enums) you can use `z.nativeEnum()`.
@@ -1504,28 +1509,49 @@ const myUnion = z.discriminatedUnion("status", [
1504
1509
  myUnion.parse({ status: "success", data: "yippie ki yay" });
1505
1510
  ```
1506
1511
 
1512
+ You can extract a reference to the array of schemas with the `.options` property.
1513
+
1514
+ ```ts
1515
+ myUnion.options; // [ZodObject<...>, ZodObject<...>]
1516
+ ```
1517
+
1518
+ To merge two or more discriminated unions, use `.options` with destructuring.
1519
+
1520
+ ```ts
1521
+ const A = z.discriminatedUnion("status", [
1522
+ /* options */
1523
+ ]);
1524
+ const B = z.discriminatedUnion("status", [
1525
+ /* options */
1526
+ ]);
1527
+
1528
+ const AB = z.discriminatedUnion("status", [...A.options, ...B.options]);
1529
+ ```
1530
+
1507
1531
  ## Records
1508
1532
 
1509
- Record schemas are used to validate types such as `{ [k: string]: number }`.
1533
+ Record schemas are used to validate types such as `Record<string, number>`. This is particularly useful for storing or caching items by ID.
1510
1534
 
1511
- If you want to validate the _values_ of an object against some schema but don't care about the keys, use `z.record(valueType)`:
1535
+ <!-- If you want to validate the _values_ of an object against some schema but don't care about the keys, use `z.record(valueType)`:
1512
1536
 
1513
1537
  ```ts
1514
1538
  const NumberCache = z.record(z.number());
1515
1539
 
1516
1540
  type NumberCache = z.infer<typeof NumberCache>;
1517
1541
  // => { [k: string]: number }
1518
- ```
1519
-
1520
- This is particularly useful for storing or caching items by ID.
1542
+ ``` -->
1521
1543
 
1522
1544
  ```ts
1523
- const userSchema = z.object({ name: z.string() });
1524
- const userStoreSchema = z.record(userSchema);
1545
+ const User = z.object({ name: z.string() });
1525
1546
 
1526
- type UserStore = z.infer<typeof userStoreSchema>;
1527
- // => type UserStore = { [ x: string ]: { name: string } }
1547
+ const UserStore = z.record(z.string(), User);
1548
+ type UserStore = z.infer<typeof UserStore>;
1549
+ // => Record<string, { name: string }>
1550
+ ```
1528
1551
 
1552
+ The schema and inferred type can be used like so:
1553
+
1554
+ ```ts
1529
1555
  const userStore: UserStore = {};
1530
1556
 
1531
1557
  userStore["77d2586b-9e8e-4ecf-8b21-ea7e0530eadd"] = {
@@ -1537,19 +1563,6 @@ userStore["77d2586b-9e8e-4ecf-8b21-ea7e0530eadd"] = {
1537
1563
  }; // TypeError
1538
1564
  ```
1539
1565
 
1540
- ### Record key type
1541
-
1542
- If you want to validate both the keys and the values, use
1543
- `z.record(keyType, valueType)`:
1544
-
1545
- ```ts
1546
- const NoEmptyKeysSchema = z.record(z.string().min(1), z.number());
1547
- NoEmptyKeysSchema.parse({ count: 1 }); // => { 'count': 1 }
1548
- NoEmptyKeysSchema.parse({ "": 1 }); // fails
1549
- ```
1550
-
1551
- _(Notice how when passing two arguments, `valueType` is the second argument)_
1552
-
1553
1566
  **A note on numerical keys**
1554
1567
 
1555
1568
  While `z.record(keyType, valueType)` is able to accept numerical key types and TypeScript's built-in Record type is `Record<KeyType, ValueType>`, it's hard to represent the TypeScript type `Record<number, any>` in Zod.
@@ -1729,7 +1742,9 @@ Thanks to [ggoodman](https://github.com/ggoodman) for suggesting this.
1729
1742
 
1730
1743
  ### Cyclical objects
1731
1744
 
1732
- Despite supporting recursive schemas, passing cyclical data into Zod will cause an infinite loop.
1745
+ Despite supporting recursive schemas, passing cyclical data into Zod will cause an infinite loop in some cases.
1746
+
1747
+ > To detect cyclical objects before they cause problems, consider [this approach](https://gist.github.com/colinhacks/d35825e505e635df27cc950776c5500b).
1733
1748
 
1734
1749
  ## Promises
1735
1750
 
@@ -1775,7 +1790,7 @@ const TestSchema = z.instanceof(Test);
1775
1790
 
1776
1791
  const blob: any = "whatever";
1777
1792
  TestSchema.parse(new Test()); // passes
1778
- TestSchema.parse("blob"); // throws
1793
+ TestSchema.parse(blob); // throws
1779
1794
  ```
1780
1795
 
1781
1796
  ## Functions
@@ -2451,7 +2466,7 @@ Note that branded types do not affect the runtime result of `.parse`. It is a st
2451
2466
  This method returns a `ZodReadonly` schema instance that parses the input using the base schema, then calls `Object.freeze()` on the result. The inferred type is also marked as `readonly`.
2452
2467
 
2453
2468
  ```ts
2454
- const schema = z.object({ name: string }).readonly();
2469
+ const schema = z.object({ name: z.string() }).readonly();
2455
2470
  type schema = z.infer<typeof schema>;
2456
2471
  // Readonly<{name: string}>
2457
2472
 
@@ -2472,7 +2487,7 @@ z.map(z.string(), z.date()).readonly();
2472
2487
  // ReadonlyMap<string, Date>
2473
2488
 
2474
2489
  z.set(z.string()).readonly();
2475
- // ReadonlySet<Promise<string>>
2490
+ // ReadonlySet<string>
2476
2491
  ```
2477
2492
 
2478
2493
  ### `.pipe`
@@ -2576,36 +2591,63 @@ type inferred = z.infer<typeof stringToNumber>; // number
2576
2591
 
2577
2592
  ### Writing generic functions
2578
2593
 
2579
- When attempting to write a function that accepts a Zod schema as an input, it's common to try something like this:
2594
+ With TypeScript generics, you can write reusable functions that accept Zod schemas as parameters. This enables you to create custom validation logic, schema transformations, and more, while maintaining type safety and inference.
2595
+
2596
+ When attempting to write a function that accepts a Zod schema as an input, it's tempting to try something like this:
2580
2597
 
2581
2598
  ```ts
2582
- function makeSchemaOptional<T>(schema: z.ZodType<T>) {
2583
- return schema.optional();
2599
+ function inferSchema<T>(schema: z.ZodType<T>) {
2600
+ return schema;
2584
2601
  }
2585
2602
  ```
2586
2603
 
2587
- This approach has some issues. The `schema` variable in this function is typed as an instance of `ZodType`, which is an abstract class that all Zod schemas inherit from. This approach loses type information, namely _which subclass_ the input actually is.
2604
+ This approach is incorrect, and limits TypeScript's ability to properly infer the argument. No matter what you pass in, the type of `schema` will be an instance of `ZodType`.
2588
2605
 
2589
2606
  ```ts
2590
- const arg = makeSchemaOptional(z.string());
2591
- arg.unwrap();
2607
+ inferSchema(z.string());
2608
+ // => ZodType<string>
2592
2609
  ```
2593
2610
 
2594
- A better approach is for the generic parameter to refer to _the schema as a whole_.
2611
+ This approach loses type information, namely _which subclass_ the input actually is (in this case, `ZodString`). That means you can't call any string-specific methods like `.min()` on the result of `inferSchema`.
2612
+
2613
+ A better approach is to infer _the schema as a whole_ instead of merely its inferred type. You can do this with a utility type called `z.ZodTypeAny`.
2595
2614
 
2596
2615
  ```ts
2597
- function makeSchemaOptional<T extends z.ZodTypeAny>(schema: T) {
2598
- return schema.optional();
2616
+ function inferSchema<T extends z.ZodTypeAny>(schema: T) {
2617
+ return schema;
2599
2618
  }
2619
+
2620
+ inferSchema(z.string());
2621
+ // => ZodString
2600
2622
  ```
2601
2623
 
2602
2624
  > `ZodTypeAny` is just a shorthand for `ZodType<any, any, any>`, a type that is broad enough to match any Zod schema.
2603
2625
 
2604
- As you can see, `schema` is now fully and properly typed.
2626
+ The Result is now fully and properly typed, and the type system can infer the specific subclass of the schema.
2627
+
2628
+ #### Inferring the inferred type
2629
+
2630
+ If you follow the best practice of using `z.ZodTypeAny` as the generic parameter for your schema, you may encounter issues with the parsed data being typed as `any` instead of the inferred type of the schema.
2605
2631
 
2606
2632
  ```ts
2607
- const arg = makeSchemaOptional(z.string());
2608
- arg.unwrap(); // ZodString
2633
+ function parseData<T extends z.ZodTypeAny>(data: unknown, schema: T) {
2634
+ return schema.parse(data);
2635
+ }
2636
+
2637
+ parseData("sup", z.string());
2638
+ // => any
2639
+ ```
2640
+
2641
+ Due to how TypeScript inference works, it is treating `schema` like a `ZodTypeAny` instead of the inferred type. You can fix this with a type cast using `z.infer`.
2642
+
2643
+ ```ts
2644
+ function parseData<T extends z.ZodTypeAny>(data: unknown, schema: T) {
2645
+ return schema.parse(data) as z.infer<T>;
2646
+ // ^^^^^^^^^^^^^^ <- add this
2647
+ }
2648
+
2649
+ parseData("sup", z.string());
2650
+ // => string
2609
2651
  ```
2610
2652
 
2611
2653
  #### Constraining allowable inputs