zod 3.22.4 → 3.23.0-beta.0
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 +360 -113
- package/lib/ZodError.d.ts +2 -1
- package/lib/ZodError.js +5 -0
- package/lib/benchmarks/datetime.d.ts +5 -0
- package/lib/benchmarks/datetime.js +54 -0
- package/lib/benchmarks/index.js +15 -2
- package/lib/benchmarks/ipv4.d.ts +5 -0
- package/lib/benchmarks/ipv4.js +54 -0
- package/lib/benchmarks/primitives.js +34 -0
- package/lib/helpers/parseUtil.d.ts +1 -1
- package/lib/helpers/parseUtil.js +15 -5
- package/lib/helpers/util.d.ts +13 -1
- package/lib/index.mjs +280 -66
- package/lib/index.umd.js +280 -65
- package/lib/types.d.ts +112 -42
- package/lib/types.js +240 -63
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -69,7 +69,9 @@
|
|
|
69
69
|
- [Coercion for primitives](#coercion-for-primitives)
|
|
70
70
|
- [Literals](#literals)
|
|
71
71
|
- [Strings](#strings)
|
|
72
|
-
- [
|
|
72
|
+
- [Datetimes](#datetimes)
|
|
73
|
+
- [Dates](#dates)
|
|
74
|
+
- [Times](#times)
|
|
73
75
|
- [IP addresses](#ip-addresses)
|
|
74
76
|
- [Numbers](#numbers)
|
|
75
77
|
- [BigInts](#bigints)
|
|
@@ -185,88 +187,135 @@ Sponsorship at any level is appreciated and encouraged. For individual developer
|
|
|
185
187
|
<table>
|
|
186
188
|
<tr>
|
|
187
189
|
<td align="center">
|
|
188
|
-
<a href="https://speakeasyapi.dev/">
|
|
190
|
+
<a href="https://speakeasyapi.dev/" target="_blank">
|
|
189
191
|
<img src="https://avatars.githubusercontent.com/u/91446104?s=200&v=4" width="200px;" alt="Speakeasy API" />
|
|
190
192
|
</a>
|
|
191
193
|
<br />
|
|
192
194
|
<b>Speakeasy</b>
|
|
193
195
|
<br />
|
|
194
|
-
<a href="https://speakeasyapi.dev/">speakeasyapi.dev</a>
|
|
196
|
+
<a href="https://speakeasyapi.dev/" target="_blank">speakeasyapi.dev</a>
|
|
195
197
|
<br />
|
|
196
|
-
<p width="200px">SDKs, Terraform, Docs.<br/>Your API made enterprise-ready
|
|
198
|
+
<p width="200px">SDKs, Terraform, Docs.<br/>Your API made enterprise-ready</p>
|
|
197
199
|
</td>
|
|
198
200
|
<td align="center">
|
|
199
|
-
<a href="https://
|
|
200
|
-
<img src="https://i.imgur.com/R0R43S2.jpg" width="200px;" alt="Glow Wallet" />
|
|
201
|
-
</a>
|
|
202
|
-
<br />
|
|
203
|
-
<b>Glow Wallet</b>
|
|
204
|
-
<br />
|
|
205
|
-
<a href="https://glow.app/">glow.app</a>
|
|
206
|
-
<br />
|
|
207
|
-
<p width="200px">Your new favorite
|
|
208
|
-
<br/>
|
|
209
|
-
Solana wallet.</p>
|
|
210
|
-
</td>
|
|
211
|
-
</tr>
|
|
212
|
-
<tr>
|
|
213
|
-
<td align="center">
|
|
214
|
-
<a href="https://deletype.com/">
|
|
201
|
+
<a href="https://deletype.com/" target="_blank">
|
|
215
202
|
<img src="https://avatars0.githubusercontent.com/u/15068039?s=200&v=4" width="200px;" alt="Deletype logo" />
|
|
216
203
|
</a>
|
|
217
204
|
<br />
|
|
218
205
|
<b>Deletype</b>
|
|
219
206
|
<br />
|
|
220
|
-
<a href="https://deletype.com">deletype.com</a>
|
|
207
|
+
<a href="https://deletype.com" target="_blank">deletype.com</a>
|
|
221
208
|
</td>
|
|
209
|
+
</tr>
|
|
210
|
+
<tr>
|
|
222
211
|
<td align="center">
|
|
223
|
-
<a href="https://trigger.dev/">
|
|
212
|
+
<a href="https://trigger.dev/" target="_blank">
|
|
224
213
|
<img src="https://avatars.githubusercontent.com/u/95297378?s=200&v=4" width="200px;" alt="Trigger.dev logo" />
|
|
225
214
|
</a>
|
|
226
215
|
<br />
|
|
227
216
|
<b>Trigger.dev</b>
|
|
228
217
|
<br />
|
|
229
|
-
<a href="https://trigger.dev">trigger.dev</a>
|
|
218
|
+
<a href="https://trigger.dev" target="_blank">trigger.dev</a>
|
|
230
219
|
<br/>
|
|
231
|
-
<p>Effortless automation for developers
|
|
220
|
+
<p>Effortless automation for developers</p>
|
|
232
221
|
</td>
|
|
233
|
-
</tr>
|
|
234
|
-
<tr>
|
|
235
222
|
<td align="center">
|
|
236
|
-
<a href="https://transloadit.com/?utm_source=zod&utm_medium=referral&utm_campaign=sponsorship&utm_content=github">
|
|
223
|
+
<a href="https://transloadit.com/?utm_source=zod&utm_medium=referral&utm_campaign=sponsorship&utm_content=github" target="_blank">
|
|
237
224
|
<img src="https://avatars.githubusercontent.com/u/125754?s=200&v=4" width="200px;" alt="Transloadit logo" />
|
|
238
225
|
</a>
|
|
239
226
|
<br />
|
|
240
227
|
<b>Transloadit</b>
|
|
241
228
|
<br />
|
|
242
|
-
<a href="https://transloadit.com/?utm_source=zod&utm_medium=referral&utm_campaign=sponsorship&utm_content=github">transloadit.com</a>
|
|
229
|
+
<a href="https://transloadit.com/?utm_source=zod&utm_medium=referral&utm_campaign=sponsorship&utm_content=github" target="_blank">transloadit.com</a>
|
|
243
230
|
<br/>
|
|
244
|
-
<p>Simple file processing for developers
|
|
231
|
+
<p>Simple file processing for developers</p>
|
|
245
232
|
</td>
|
|
233
|
+
</tr>
|
|
234
|
+
<tr>
|
|
246
235
|
<td align="center">
|
|
247
|
-
<a href="https://infisical.com">
|
|
236
|
+
<a href="https://infisical.com" target="_blank">
|
|
248
237
|
<img src="https://avatars.githubusercontent.com/u/107880645?s=200&v=4" width="200px;" alt="Infisical logo" />
|
|
249
238
|
</a>
|
|
250
239
|
<br />
|
|
251
240
|
<b>Infisical</b>
|
|
252
241
|
<br />
|
|
253
|
-
<a href="https://infisical.com">infisical.com</a>
|
|
242
|
+
<a href="https://infisical.com" target="_blank">infisical.com</a>
|
|
254
243
|
<br/>
|
|
255
|
-
<p>Open-source platform for secret<br/>management: sync secrets across your<br/>team/infrastructure and prevent secret leaks
|
|
244
|
+
<p>Open-source platform for secret<br/>management: sync secrets across your<br/>team/infrastructure and prevent secret leaks</p>
|
|
256
245
|
</td>
|
|
257
|
-
</tr>
|
|
258
|
-
<tr>
|
|
259
246
|
<td align="center">
|
|
260
|
-
<a href="https://whop.com/">
|
|
247
|
+
<a href="https://whop.com/" target="_blank">
|
|
261
248
|
<img src="https://avatars.githubusercontent.com/u/91036480?s=200&v=4" width="200px;" alt="Whop logo" />
|
|
262
249
|
</a>
|
|
263
250
|
<br />
|
|
264
251
|
<b>Whop</b>
|
|
265
252
|
<br />
|
|
266
|
-
<a href="https://whop.com/">whop.com</a>
|
|
253
|
+
<a href="https://whop.com/" target="_blank">whop.com</a>
|
|
254
|
+
<br />
|
|
255
|
+
<p width="200px">A marketplace for really cool internet products</p>
|
|
256
|
+
</td>
|
|
257
|
+
</tr>
|
|
258
|
+
<tr>
|
|
259
|
+
<td align="center">
|
|
260
|
+
<a href="https://cryptojobslist.com/" target="_blank">
|
|
261
|
+
<img src="https://avatars.githubusercontent.com/u/36402888?s=200&v=4" width="200px;" alt="CryptoJobsList logo" />
|
|
262
|
+
</a>
|
|
263
|
+
<br />
|
|
264
|
+
<b>CryptoJobsList</b>
|
|
265
|
+
<br />
|
|
266
|
+
<a href="https://cryptojobslist.com/" target="_blank">cryptojobslist.com</a>
|
|
267
|
+
<br />
|
|
268
|
+
<p width="200px">The biggest list of crypto, blockchain and Web3 jobs</p>
|
|
269
|
+
</td>
|
|
270
|
+
<td align="center">
|
|
271
|
+
<a href="https://plain.com/" target="_blank">
|
|
272
|
+
<img src="https://avatars.githubusercontent.com/u/70170949?s=200&v=4" width="200px;" alt="Plain logo" />
|
|
273
|
+
</a>
|
|
274
|
+
<br />
|
|
275
|
+
<b>Plain.</b>
|
|
276
|
+
<br />
|
|
277
|
+
<a href="https://plain.com/" target="_blank">plain.com</a>
|
|
278
|
+
<br />
|
|
279
|
+
<p width="200px">How developers support their users</p>
|
|
280
|
+
</td>
|
|
281
|
+
</tr>
|
|
282
|
+
<tr>
|
|
283
|
+
<td align="center">
|
|
284
|
+
<a href="https://inngest.com/" target="_blank">
|
|
285
|
+
<img src="https://avatars.githubusercontent.com/u/78935958?s=200&v=4" width="200px;" alt="Inngest logo" />
|
|
286
|
+
</a>
|
|
287
|
+
<br />
|
|
288
|
+
<b>Inngest</b>
|
|
289
|
+
<br />
|
|
290
|
+
<a href="https://inngest.com/" target="_blank">inngest.com</a>
|
|
291
|
+
<br />
|
|
292
|
+
<p width="200px">Serverless queues + durable workflows for TypeScript</p>
|
|
293
|
+
</td>
|
|
294
|
+
<td align="center">
|
|
295
|
+
<a href="https://storyblok.com/" target="_blank">
|
|
296
|
+
<img src="https://avatars.githubusercontent.com/u/13880908?s=200&v=4" width="200px;" alt="Storyblok CMS" />
|
|
297
|
+
</a>
|
|
298
|
+
<br />
|
|
299
|
+
<b>Storyblok</b>
|
|
300
|
+
<br />
|
|
301
|
+
<a href="https://storyblok.com/" target="_blank">storyblok.com</a>
|
|
302
|
+
<br />
|
|
303
|
+
<p width="200px">The only headless CMS with a visual editor</p>
|
|
304
|
+
</td>
|
|
305
|
+
</tr>
|
|
306
|
+
<tr>
|
|
307
|
+
<td align="center">
|
|
308
|
+
<a href="https://mux.link/zod" target="_blank">
|
|
309
|
+
<img src="https://avatars.githubusercontent.com/u/16199997?s=200&v=4" width="200px;" alt="Mux logo" />
|
|
310
|
+
</a>
|
|
311
|
+
<br />
|
|
312
|
+
<b>Mux</b>
|
|
267
313
|
<br />
|
|
268
|
-
<
|
|
314
|
+
<a href="https://mux.link/zod" target="_blank">mux.com</a>
|
|
315
|
+
<br />
|
|
316
|
+
<p width="200px">The internet's video infrastructure</p>
|
|
269
317
|
</td>
|
|
318
|
+
</tr>
|
|
270
319
|
</table>
|
|
271
320
|
|
|
272
321
|
#### Silver
|
|
@@ -274,51 +323,51 @@ Sponsorship at any level is appreciated and encouraged. For individual developer
|
|
|
274
323
|
<table>
|
|
275
324
|
<tr>
|
|
276
325
|
<td align="center" colspan="2">
|
|
277
|
-
<a href="https://www.numeric.io">
|
|
326
|
+
<a href="https://www.numeric.io" target="_blank">
|
|
278
327
|
<img src="https://i.imgur.com/kTiLtZt.png" width="250px;" alt="Numeric logo" />
|
|
279
328
|
</a>
|
|
280
329
|
<br />
|
|
281
330
|
<b>Numeric</b>
|
|
282
331
|
<br />
|
|
283
|
-
<a href="https://www.numeric.io">numeric.io</a>
|
|
332
|
+
<a href="https://www.numeric.io" target="_blank">numeric.io</a>
|
|
284
333
|
</td>
|
|
285
334
|
<td align="center">
|
|
286
|
-
<a href="https://marcatopartners.com/">
|
|
335
|
+
<a href="https://marcatopartners.com/" target="_blank">
|
|
287
336
|
<img src="https://avatars.githubusercontent.com/u/84106192?s=200&v=4" width="150px;" alt="Marcato Partners" />
|
|
288
337
|
</a>
|
|
289
338
|
<br />
|
|
290
339
|
<b>Marcato Partners</b>
|
|
291
340
|
<br />
|
|
292
|
-
<a href="https://marcatopartners.com/">marcatopartners.com</a>
|
|
341
|
+
<a href="https://marcatopartners.com/" target="_blank">marcatopartners.com</a>
|
|
293
342
|
</td>
|
|
294
343
|
</tr>
|
|
295
344
|
<tr>
|
|
296
345
|
<td align="center">
|
|
297
|
-
<a href="https://interval.com">
|
|
346
|
+
<a href="https://interval.com" target="_blank">
|
|
298
347
|
<img src="https://avatars.githubusercontent.com/u/67802063?s=200&v=4" width="150px;" alt="" />
|
|
299
348
|
</a>
|
|
300
349
|
<br />
|
|
301
350
|
<b>Interval</b>
|
|
302
351
|
<br />
|
|
303
|
-
<a href="https://interval.com">interval.com</a>
|
|
352
|
+
<a href="https://interval.com" target="_blank">interval.com</a>
|
|
304
353
|
</td>
|
|
305
354
|
<td align="center">
|
|
306
|
-
<a href="https://seasoned.cc">
|
|
355
|
+
<a href="https://seasoned.cc" target="_blank">
|
|
307
356
|
<img src="https://avatars.githubusercontent.com/u/33913103?s=200&v=4" width="150px;" alt="" />
|
|
308
357
|
</a>
|
|
309
358
|
<br />
|
|
310
359
|
<b>Seasoned Software</b>
|
|
311
360
|
<br />
|
|
312
|
-
<a href="https://seasoned.cc">seasoned.cc</a>
|
|
361
|
+
<a href="https://seasoned.cc" target="_blank">seasoned.cc</a>
|
|
313
362
|
</td>
|
|
314
363
|
<td align="center">
|
|
315
|
-
<a href="https://www.bamboocreative.nz/">
|
|
364
|
+
<a href="https://www.bamboocreative.nz/" target="_blank">
|
|
316
365
|
<img src="https://avatars.githubusercontent.com/u/41406870?v=4" width="150px;" alt="Bamboo Creative logo" />
|
|
317
366
|
</a>
|
|
318
367
|
<br />
|
|
319
368
|
<b>Bamboo Creative</b>
|
|
320
369
|
<br />
|
|
321
|
-
<a href="https://www.bamboocreative.nz">bamboocreative.nz</a>
|
|
370
|
+
<a href="https://www.bamboocreative.nz" target="_blank">bamboocreative.nz</a>
|
|
322
371
|
</td>
|
|
323
372
|
</tr>
|
|
324
373
|
</table>
|
|
@@ -328,98 +377,174 @@ Sponsorship at any level is appreciated and encouraged. For individual developer
|
|
|
328
377
|
<table>
|
|
329
378
|
<tr>
|
|
330
379
|
<td align="center">
|
|
331
|
-
<a href="https://twitter.com/flybayer">
|
|
380
|
+
<a href="https://twitter.com/flybayer" target="_blank">
|
|
332
381
|
<img src="https://avatars2.githubusercontent.com/u/8813276?s=460&u=4ff8beb9a67b173015c4b426a92d89cab960af1b&v=4" width="100px;" alt=""/>
|
|
333
382
|
</a>
|
|
334
383
|
<br />
|
|
335
384
|
<b>Brandon Bayer</b>
|
|
336
385
|
<br/>
|
|
337
|
-
<a href="https://twitter.com/flybayer">@flybayer</a>,
|
|
386
|
+
<a href="https://twitter.com/flybayer" target="_blank">@flybayer</a>,
|
|
338
387
|
<span>creator of <a href="https://blitzjs.com">Blitz.js</a></span>
|
|
339
388
|
<br />
|
|
340
389
|
</td>
|
|
341
390
|
<td align="center">
|
|
342
|
-
<a href="https://github.com/brabeji">
|
|
391
|
+
<a href="https://github.com/brabeji" target="_blank">
|
|
343
392
|
<img src="https://avatars.githubusercontent.com/u/2237954?v=4" width="100px;" alt=""/>
|
|
344
393
|
</a>
|
|
345
394
|
<br />
|
|
346
395
|
<b>Jiří Brabec</b>
|
|
347
396
|
<br/>
|
|
348
|
-
<a href="https://github.com/brabeji">@brabeji</a>
|
|
397
|
+
<a href="https://github.com/brabeji" target="_blank">@brabeji</a>
|
|
349
398
|
<br />
|
|
350
399
|
</td>
|
|
351
400
|
<td align="center">
|
|
352
|
-
<a href="https://twitter.com/alexdotjs">
|
|
401
|
+
<a href="https://twitter.com/alexdotjs" target="_blank">
|
|
353
402
|
<img src="https://avatars.githubusercontent.com/u/459267?v=4" width="100px;" alt="" />
|
|
354
403
|
</a>
|
|
355
404
|
<br />
|
|
356
405
|
<b>Alex Johansson</b>
|
|
357
406
|
<br />
|
|
358
|
-
<a href="https://twitter.com/alexdotjs">@alexdotjs</a>
|
|
407
|
+
<a href="https://twitter.com/alexdotjs" target="_blank">@alexdotjs</a>
|
|
359
408
|
</td>
|
|
360
409
|
</tr>
|
|
361
410
|
<tr>
|
|
362
411
|
<td align="center">
|
|
363
|
-
<a href="https://fungible.systems/">
|
|
412
|
+
<a href="https://fungible.systems/" target="_blank">
|
|
364
413
|
<img src="https://avatars.githubusercontent.com/u/80220121?s=200&v=4" width="100px;" alt="Fungible Systems logo"/>
|
|
365
414
|
</a>
|
|
366
415
|
<br />
|
|
367
416
|
<b>Fungible Systems</b>
|
|
368
417
|
<br/>
|
|
369
|
-
<a href="https://fungible.systems/">fungible.systems</a>
|
|
418
|
+
<a href="https://fungible.systems/" target="_blank">fungible.systems</a>
|
|
370
419
|
<br />
|
|
371
420
|
</td>
|
|
372
421
|
<td align="center">
|
|
373
|
-
<a href="https://adaptable.io/">
|
|
422
|
+
<a href="https://adaptable.io/" target="_blank">
|
|
374
423
|
<img src="https://avatars.githubusercontent.com/u/60378268?s=200&v=4" width="100px;" alt=""/>
|
|
375
424
|
</a>
|
|
376
425
|
<br />
|
|
377
426
|
<b>Adaptable</b>
|
|
378
427
|
<br/>
|
|
379
|
-
<a href="https://adaptable.io/">adaptable.io</a>
|
|
428
|
+
<a href="https://adaptable.io/" target="_blank">adaptable.io</a>
|
|
380
429
|
<br />
|
|
381
430
|
</td>
|
|
382
431
|
<td align="center">
|
|
383
|
-
<a href="https://www.avanawallet.com/">
|
|
432
|
+
<a href="https://www.avanawallet.com/" target="_blank">
|
|
384
433
|
<img src="https://avatars.githubusercontent.com/u/105452197?s=200&v=4" width="100px;" alt="Avana Wallet logo"/>
|
|
385
434
|
</a>
|
|
386
435
|
<br />
|
|
387
436
|
<b>Avana Wallet</b>
|
|
388
437
|
<br/>
|
|
389
|
-
<a href="https://www.avanawallet.com/">avanawallet.com</a><br/>
|
|
438
|
+
<a href="https://www.avanawallet.com/" target="_blank">avanawallet.com</a><br/>
|
|
390
439
|
<span>Solana non-custodial wallet</span>
|
|
391
440
|
<br />
|
|
392
441
|
</td>
|
|
393
442
|
</tr>
|
|
394
443
|
<tr>
|
|
395
444
|
<td align="center">
|
|
396
|
-
<a href="https://learnwithjason.dev">
|
|
445
|
+
<a href="https://learnwithjason.dev" target="_blank">
|
|
397
446
|
<img src="https://avatars.githubusercontent.com/u/66575486?s=200&v=4" width="100px;" alt="Learn with Jason logo"/>
|
|
398
447
|
</a>
|
|
399
448
|
<br />
|
|
400
449
|
<b>Jason Lengstorf</b>
|
|
401
450
|
<br/>
|
|
402
|
-
<a href="https://learnwithjason.dev/">learnwithjason.dev</a>
|
|
451
|
+
<a href="https://learnwithjason.dev/" target="_blank">learnwithjason.dev</a>
|
|
403
452
|
<br />
|
|
404
453
|
</td>
|
|
405
454
|
<td align="center">
|
|
406
|
-
<a href="https://ill.inc/">
|
|
455
|
+
<a href="https://ill.inc/" target="_blank">
|
|
407
456
|
<img src="https://avatars.githubusercontent.com/u/89107581?s=200&v=4" width="100px;" alt="Global Illumination"/>
|
|
408
457
|
</a>
|
|
409
458
|
<br />
|
|
410
459
|
<b>Global Illumination, Inc.</b>
|
|
411
460
|
<br/>
|
|
412
|
-
<a href="https://ill.inc/">ill.inc</a>
|
|
461
|
+
<a href="https://ill.inc/" target="_blank">ill.inc</a>
|
|
413
462
|
<br />
|
|
414
463
|
</td>
|
|
415
|
-
|
|
416
|
-
<a href="https://www.masterborn.com/career?utm_source=github&utm_medium=referral&utm_campaign=zodsponsoring">
|
|
464
|
+
<td align="center">
|
|
465
|
+
<a href="https://www.masterborn.com/career?utm_source=github&utm_medium=referral&utm_campaign=zodsponsoring" target="_blank">
|
|
417
466
|
<img src="https://avatars.githubusercontent.com/u/48984031?s=200&v=4" width="100px;" alt="MasterBorn logo"/>
|
|
418
467
|
</a>
|
|
419
468
|
<br />
|
|
420
469
|
<b>MasterBorn</b>
|
|
421
470
|
<br/>
|
|
422
|
-
<a href="https://www.masterborn.com/career?utm_source=github&utm_medium=referral&utm_campaign=zodsponsoring">masterborn.com</a>
|
|
471
|
+
<a href="https://www.masterborn.com/career?utm_source=github&utm_medium=referral&utm_campaign=zodsponsoring" target="_blank">masterborn.com</a>
|
|
472
|
+
<br />
|
|
473
|
+
</td>
|
|
474
|
+
</tr>
|
|
475
|
+
<tr>
|
|
476
|
+
<td align="center">
|
|
477
|
+
<a href="https://github.com/kronodeus" target="_blank">
|
|
478
|
+
<img src="https://avatars.githubusercontent.com/u/18314366?v=4" width="100px;" alt="Ryan Palmer"/>
|
|
479
|
+
</a>
|
|
480
|
+
<br />
|
|
481
|
+
<b>Ryan Palmer</b>
|
|
482
|
+
<br/>
|
|
483
|
+
<a href="https://github.com/kronodeus" target="_blank">@kronodeus</a>
|
|
484
|
+
<br />
|
|
485
|
+
</td>
|
|
486
|
+
<td align="center">
|
|
487
|
+
<a href="https://github.com/overthemike" target="_blank">
|
|
488
|
+
<img src="https://avatars.githubusercontent.com/u/223509?v=4" width="100px;" alt="Michael Sweeney"/>
|
|
489
|
+
</a>
|
|
490
|
+
<br />
|
|
491
|
+
<b>Michael Sweeney</b>
|
|
492
|
+
<br/>
|
|
493
|
+
<a href="https://github.com/overthemike" target="_blank">@overthemike</a>
|
|
494
|
+
<br />
|
|
495
|
+
</td>
|
|
496
|
+
<td align="center">
|
|
497
|
+
<a href="https://usenextbase.com/" target="_blank">
|
|
498
|
+
<img src="https://pbs.twimg.com/profile_images/1692236063409070080/28yXFtop_400x400.jpg" width="100px;" alt="Nextbase logo"/>
|
|
499
|
+
</a>
|
|
500
|
+
<br />
|
|
501
|
+
<b>Nextbase</b>
|
|
502
|
+
<br/>
|
|
503
|
+
<a href="https://usenextbase.com/" target="_blank">usenextbase.com</a>
|
|
504
|
+
<br />
|
|
505
|
+
</td>
|
|
506
|
+
</tr>
|
|
507
|
+
<tr>
|
|
508
|
+
<td align="center">
|
|
509
|
+
<a href="https://remotion.dev" target="_blank">
|
|
510
|
+
<img src="https://avatars.githubusercontent.com/u/85344006" width="100px;" alt="Remotion logo"/>
|
|
511
|
+
</a>
|
|
512
|
+
<br />
|
|
513
|
+
<b>Remotion</b>
|
|
514
|
+
<br/>
|
|
515
|
+
<a href="https://remotion.dev" target="_blank">remotion.dev</a>
|
|
516
|
+
<br />
|
|
517
|
+
</td>
|
|
518
|
+
<td align="center">
|
|
519
|
+
<a href="https://github.com/ConnorSinnott" target="_blank">
|
|
520
|
+
<img src="https://avatars.githubusercontent.com/u/12754711?v=4" width="100px;" alt="Connor Sinnott profile"/>
|
|
521
|
+
</a>
|
|
522
|
+
<br />
|
|
523
|
+
<b>Connor Sinnott</b>
|
|
524
|
+
<br/>
|
|
525
|
+
<a href="https://github.com/ConnorSinnott" target="_blank">@ConnorSinnott</a>
|
|
526
|
+
<br />
|
|
527
|
+
</td>
|
|
528
|
+
<td align="center">
|
|
529
|
+
<a href="https://aerabi.com/" target="_blank">
|
|
530
|
+
<img src="https://avatars.githubusercontent.com/u/44623032?v=4" width="100px;" alt="aerabi"/>
|
|
531
|
+
</a>
|
|
532
|
+
<br />
|
|
533
|
+
<b>Mohammad-Ali A'râbi</b>
|
|
534
|
+
<br/>
|
|
535
|
+
<a href="https://aerabi.com/" target="_blank">aerabi.com</a>
|
|
536
|
+
<br />
|
|
537
|
+
</td>
|
|
538
|
+
</tr>
|
|
539
|
+
<tr>
|
|
540
|
+
<td align="center">
|
|
541
|
+
<a href="https://supatool.io/" target="_blank">
|
|
542
|
+
<img src="https://github.com/colinhacks/zod/assets/3084745/d0ec96c9-bf79-494f-9caa-9233da251f55" width="100px;" alt="Supatool logo"/>
|
|
543
|
+
</a>
|
|
544
|
+
<br />
|
|
545
|
+
<b>Supatool</b>
|
|
546
|
+
<br/>
|
|
547
|
+
<a href="https://supatool.io/" target="_blank">supatool.io</a>
|
|
423
548
|
<br />
|
|
424
549
|
</td>
|
|
425
550
|
</tr>
|
|
@@ -439,6 +564,7 @@ There are a growing number of tools that are built atop or support Zod natively!
|
|
|
439
564
|
- [`tRPC`](https://github.com/trpc/trpc): Build end-to-end typesafe APIs without GraphQL.
|
|
440
565
|
- [`@anatine/zod-nestjs`](https://github.com/anatine/zod-plugins/tree/main/packages/zod-nestjs): Helper methods for using Zod in a NestJS project.
|
|
441
566
|
- [`zod-endpoints`](https://github.com/flock-community/zod-endpoints): Contract-first strictly typed endpoints with Zod. OpenAPI compatible.
|
|
567
|
+
- [`zhttp`](https://github.com/evertdespiegeleer/zhttp): An OpenAPI compatible, strictly typed http library with Zod input and response validation.
|
|
442
568
|
- [`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
569
|
- [`@zodios/core`](https://github.com/ecyrbe/zodios): A typescript API client with runtime and compile time validation backed by axios and zod.
|
|
444
570
|
- [`express-zod-api`](https://github.com/RobinTail/express-zod-api): Build Express-based APIs with I/O schema validation and custom middlewares.
|
|
@@ -447,12 +573,12 @@ There are a growing number of tools that are built atop or support Zod natively!
|
|
|
447
573
|
|
|
448
574
|
#### Form integrations
|
|
449
575
|
|
|
450
|
-
- [`conform`](https://conform.guide/api/zod): A progressive enhancement first form validation library for Remix and React Router
|
|
451
576
|
- [`react-hook-form`](https://github.com/react-hook-form/resolvers#zod): A first-party Zod resolver for React Hook Form.
|
|
452
577
|
- [`zod-validation-error`](https://github.com/causaly/zod-validation-error): Generate user-friendly error messages from `ZodError`s.
|
|
453
578
|
- [`zod-formik-adapter`](https://github.com/robertLichtnow/zod-formik-adapter): A community-maintained Formik adapter for Zod.
|
|
454
579
|
- [`react-zorm`](https://github.com/esamattis/react-zorm): Standalone `<form>` generation and validation for React using Zod.
|
|
455
580
|
- [`zodix`](https://github.com/rileytomasek/zodix): Zod utilities for FormData and URLSearchParams in Remix loaders and actions.
|
|
581
|
+
- [`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
582
|
- [`remix-params-helper`](https://github.com/kiliman/remix-params-helper): Simplify integration of Zod with standard URLSearchParams and FormData for Remix apps.
|
|
457
583
|
- [`formik-validator-zod`](https://github.com/glazy/formik-validator-zod): Formik-compliant validator library that simplifies using Zod with Formik.
|
|
458
584
|
- [`zod-i18n-map`](https://github.com/aiji42/zod-i18n): Useful for translating Zod error messages.
|
|
@@ -490,6 +616,8 @@ There are a growing number of tools that are built atop or support Zod natively!
|
|
|
490
616
|
- [`zod-prisma-types`](https://github.com/chrishoermann/zod-prisma-types) Create Zod types from your Prisma models.
|
|
491
617
|
- [`quicktype`](https://app.quicktype.io/): Convert JSON objects and JSON schemas into Zod schemas.
|
|
492
618
|
- [`@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).
|
|
619
|
+
- [`java-to-zod`](https://github.com/ivangreene/java-to-zod): Convert POJOs to Zod schemas
|
|
620
|
+
- [`Orval`](https://github.com/anymaniax/orval): Generate Zod schemas from OpenAPI schemas
|
|
493
621
|
|
|
494
622
|
#### Mocking
|
|
495
623
|
|
|
@@ -504,13 +632,17 @@ There are a growing number of tools that are built atop or support Zod natively!
|
|
|
504
632
|
- [`freerstore`](https://github.com/JacobWeisenburger/freerstore): Firestore cost optimizer.
|
|
505
633
|
- [`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
634
|
- [`soly`](https://github.com/mdbetancourt/soly): Create CLI applications with zod.
|
|
635
|
+
- [`pastel`](https://github.com/vadimdemedes/pastel): Create CLI applications with react, zod, and ink.
|
|
507
636
|
- [`zod-xlsx`](https://github.com/sidwebworks/zod-xlsx): A xlsx based resource validator using Zod schemas.
|
|
508
637
|
- [`znv`](https://github.com/lostfictions/znv): Type-safe environment parsing and validation for Node.js with Zod schemas.
|
|
638
|
+
- [`zod-config`](https://github.com/alexmarqs/zod-config): Load configurations across multiple sources with flexible adapters, ensuring type safety with Zod.
|
|
509
639
|
|
|
510
640
|
#### Utilities for Zod
|
|
511
641
|
|
|
512
642
|
- [`zod_utilz`](https://github.com/JacobWeisenburger/zod_utilz): Framework agnostic utilities for Zod.
|
|
513
643
|
- [`zod-sandbox`](https://github.com/nereumelo/zod-sandbox): Controlled environment for testing zod schemas. [Live demo](https://zod-sandbox.vercel.app/).
|
|
644
|
+
- [`zod-dev`](https://github.com/schalkventer/zod-dev): Conditionally disables Zod runtime parsing in production.
|
|
645
|
+
- [`zod-accelerator`](https://github.com/duplojs/duplojs-zod-accelerator): Accelerates Zod's throughput up to ~100x.
|
|
514
646
|
|
|
515
647
|
## Installation
|
|
516
648
|
|
|
@@ -635,16 +767,19 @@ Zod now provides a more convenient way to coerce primitive values.
|
|
|
635
767
|
const schema = z.coerce.string();
|
|
636
768
|
schema.parse("tuna"); // => "tuna"
|
|
637
769
|
schema.parse(12); // => "12"
|
|
638
|
-
schema.parse(true); // => "true"
|
|
639
770
|
```
|
|
640
771
|
|
|
641
|
-
During the parsing step, the input is passed through the `String()` function, which is a JavaScript built-in for coercing data into strings.
|
|
772
|
+
During the parsing step, the input is passed through the `String()` function, which is a JavaScript built-in for coercing data into strings.
|
|
773
|
+
|
|
774
|
+
The returned schema is a normal `ZodString` instance so you can use all string methods.
|
|
642
775
|
|
|
643
776
|
```ts
|
|
644
777
|
z.coerce.string().email().min(5);
|
|
645
778
|
```
|
|
646
779
|
|
|
647
|
-
|
|
780
|
+
**How coercion works**
|
|
781
|
+
|
|
782
|
+
All primitive types support coercion. Zod coerces all inputs using the built-in constructors: `String(input)`, `Number(input)`, `new Date(input)`, etc.
|
|
648
783
|
|
|
649
784
|
```ts
|
|
650
785
|
z.coerce.string(); // String(input)
|
|
@@ -654,9 +789,19 @@ z.coerce.bigint(); // BigInt(input)
|
|
|
654
789
|
z.coerce.date(); // new Date(input)
|
|
655
790
|
```
|
|
656
791
|
|
|
792
|
+
Note that some behavior may not be what you expect.
|
|
793
|
+
|
|
794
|
+
```ts
|
|
795
|
+
schema.parse(true); // => "true"
|
|
796
|
+
schema.parse(undefined); // => "undefined"
|
|
797
|
+
schema.parse(null); // => "null"
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
For more control over coercion logic, consider using [`z.preprocess`](#preprocess) or [`z.pipe()`](#pipe).
|
|
801
|
+
|
|
657
802
|
**Boolean coercion**
|
|
658
803
|
|
|
659
|
-
Zod's
|
|
804
|
+
Zod's approach to 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`.
|
|
660
805
|
|
|
661
806
|
```ts
|
|
662
807
|
z.coerce.boolean().parse("tuna"); // => true
|
|
@@ -666,6 +811,7 @@ z.coerce.boolean().parse(1); // => true
|
|
|
666
811
|
z.coerce.boolean().parse([]); // => true
|
|
667
812
|
|
|
668
813
|
z.coerce.boolean().parse(0); // => false
|
|
814
|
+
z.coerce.boolean().parse(""); // => false
|
|
669
815
|
z.coerce.boolean().parse(undefined); // => false
|
|
670
816
|
z.coerce.boolean().parse(null); // => false
|
|
671
817
|
```
|
|
@@ -702,6 +848,7 @@ z.string().email();
|
|
|
702
848
|
z.string().url();
|
|
703
849
|
z.string().emoji();
|
|
704
850
|
z.string().uuid();
|
|
851
|
+
z.string().nanoid();
|
|
705
852
|
z.string().cuid();
|
|
706
853
|
z.string().cuid2();
|
|
707
854
|
z.string().ulid();
|
|
@@ -709,10 +856,14 @@ z.string().regex(regex);
|
|
|
709
856
|
z.string().includes(string);
|
|
710
857
|
z.string().startsWith(string);
|
|
711
858
|
z.string().endsWith(string);
|
|
712
|
-
z.string().datetime(); // ISO 8601; default
|
|
713
|
-
z.string().
|
|
714
|
-
|
|
715
|
-
//
|
|
859
|
+
z.string().datetime(); // ISO 8601; by default only `Z` timezone allowed
|
|
860
|
+
z.string().date(); // ISO date format (YYYY-MM-DD)
|
|
861
|
+
z.string().time(); // ISO time format (HH:mm:ss[.SSSSSS])
|
|
862
|
+
z.string().duration(); // ISO 8601 duration
|
|
863
|
+
z.string().ip(); // defaults to allow both IPv4 and IPv6
|
|
864
|
+
z.string().base64();
|
|
865
|
+
|
|
866
|
+
// transforms
|
|
716
867
|
z.string().trim(); // trim whitespace
|
|
717
868
|
z.string().toLowerCase(); // toLowerCase
|
|
718
869
|
z.string().toUpperCase(); // toUpperCase
|
|
@@ -743,10 +894,18 @@ z.string().includes("tuna", { message: "Must include tuna" });
|
|
|
743
894
|
z.string().startsWith("https://", { message: "Must provide secure URL" });
|
|
744
895
|
z.string().endsWith(".com", { message: "Only .com domains allowed" });
|
|
745
896
|
z.string().datetime({ message: "Invalid datetime string! Must be UTC." });
|
|
897
|
+
z.string().date({ message: "Invalid date string!" });
|
|
898
|
+
z.string().time({ message: "Invalid time string!" });
|
|
746
899
|
z.string().ip({ message: "Invalid IP address" });
|
|
747
900
|
```
|
|
748
901
|
|
|
749
|
-
###
|
|
902
|
+
### Datetimes
|
|
903
|
+
|
|
904
|
+
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.
|
|
905
|
+
|
|
906
|
+
The `z.string().date()` method validates strings in the format `YYYY-MM-DD`.
|
|
907
|
+
|
|
908
|
+
The `z.string().time()` method validates strings in the format `HH:mm:ss[.SSSSSS][Z|(+|-)hh[:]mm]` (the time portion of [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)). It defaults to `HH:mm:ss[.SSSSSS]` validation: no timezone offsets or `Z`, with arbitrary sub-second decimal.
|
|
750
909
|
|
|
751
910
|
The `z.string().datetime()` method enforces ISO 8601; default is no timezone offsets and arbitrary sub-second decimal precision.
|
|
752
911
|
|
|
@@ -779,6 +938,49 @@ const datetime = z.string().datetime({ precision: 3 });
|
|
|
779
938
|
datetime.parse("2020-01-01T00:00:00.123Z"); // pass
|
|
780
939
|
datetime.parse("2020-01-01T00:00:00Z"); // fail
|
|
781
940
|
datetime.parse("2020-01-01T00:00:00.123456Z"); // fail
|
|
941
|
+
|
|
942
|
+
const time = z.string().time({ precision: 3 });
|
|
943
|
+
|
|
944
|
+
time.parse("00:00:00.123"); // pass
|
|
945
|
+
time.parse("00:00:00"); // fail
|
|
946
|
+
time.parse("00:00:00.123456"); // fail
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
### Dates
|
|
950
|
+
|
|
951
|
+
The `z.string().date()` method validates strings in the format `YYYY-MM-DD`.
|
|
952
|
+
|
|
953
|
+
```ts
|
|
954
|
+
const date = z.string().date();
|
|
955
|
+
|
|
956
|
+
date.parse("2020-01-01"); // pass
|
|
957
|
+
date.parse("2020-1-1"); // fail
|
|
958
|
+
date.parse("2020-01-32"); // fail
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
### Times
|
|
962
|
+
|
|
963
|
+
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.
|
|
964
|
+
|
|
965
|
+
```ts
|
|
966
|
+
const time = z.string().time();
|
|
967
|
+
|
|
968
|
+
time.parse("00:00:00"); // pass
|
|
969
|
+
time.parse("09:52:31"); // pass
|
|
970
|
+
time.parse("23:59:59.9999999"); // pass (arbitrary precision)
|
|
971
|
+
|
|
972
|
+
time.parse("00:00:00.123Z"); // fail (no `Z` allowed)
|
|
973
|
+
time.parse("00:00:00.123+02:00"); // fail (no offsets allowed)
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
You can set the `precision` option to constrain the allowable decimal precision.
|
|
977
|
+
|
|
978
|
+
```ts
|
|
979
|
+
const time = z.string().time({ precision: 3 });
|
|
980
|
+
|
|
981
|
+
time.parse("00:00:00.123"); // pass
|
|
982
|
+
time.parse("00:00:00.123456"); // fail
|
|
983
|
+
time.parse("00:00:00"); // fail
|
|
782
984
|
```
|
|
783
985
|
|
|
784
986
|
### IP addresses
|
|
@@ -869,7 +1071,7 @@ You can customize certain error messages when creating a nan schema.
|
|
|
869
1071
|
```ts
|
|
870
1072
|
const isNaN = z.nan({
|
|
871
1073
|
required_error: "isNaN is required",
|
|
872
|
-
invalid_type_error: "isNaN must be not a number",
|
|
1074
|
+
invalid_type_error: "isNaN must be 'not a number'",
|
|
873
1075
|
});
|
|
874
1076
|
```
|
|
875
1077
|
|
|
@@ -953,7 +1155,7 @@ const fish = ["Salmon", "Tuna", "Trout"];
|
|
|
953
1155
|
const FishEnum = z.enum(fish);
|
|
954
1156
|
```
|
|
955
1157
|
|
|
956
|
-
|
|
1158
|
+
**`.enum`**
|
|
957
1159
|
|
|
958
1160
|
To get autocompletion with a Zod enum, use the `.enum` property of your schema:
|
|
959
1161
|
|
|
@@ -976,6 +1178,16 @@ You can also retrieve the list of options as a tuple with the `.options` propert
|
|
|
976
1178
|
FishEnum.options; // ["Salmon", "Tuna", "Trout"];
|
|
977
1179
|
```
|
|
978
1180
|
|
|
1181
|
+
**`.exclude/.extract()`**
|
|
1182
|
+
|
|
1183
|
+
You can create subsets of a Zod enum with the `.exclude` and `.extract` methods.
|
|
1184
|
+
|
|
1185
|
+
```ts
|
|
1186
|
+
const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]);
|
|
1187
|
+
const SalmonAndTrout = FishEnum.extract(["Salmon", "Trout"]);
|
|
1188
|
+
const TunaOnly = FishEnum.exclude(["Salmon", "Trout"]);
|
|
1189
|
+
```
|
|
1190
|
+
|
|
979
1191
|
## Native enums
|
|
980
1192
|
|
|
981
1193
|
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 +1716,49 @@ const myUnion = z.discriminatedUnion("status", [
|
|
|
1504
1716
|
myUnion.parse({ status: "success", data: "yippie ki yay" });
|
|
1505
1717
|
```
|
|
1506
1718
|
|
|
1719
|
+
You can extract a reference to the array of schemas with the `.options` property.
|
|
1720
|
+
|
|
1721
|
+
```ts
|
|
1722
|
+
myUnion.options; // [ZodObject<...>, ZodObject<...>]
|
|
1723
|
+
```
|
|
1724
|
+
|
|
1725
|
+
To merge two or more discriminated unions, use `.options` with destructuring.
|
|
1726
|
+
|
|
1727
|
+
```ts
|
|
1728
|
+
const A = z.discriminatedUnion("status", [
|
|
1729
|
+
/* options */
|
|
1730
|
+
]);
|
|
1731
|
+
const B = z.discriminatedUnion("status", [
|
|
1732
|
+
/* options */
|
|
1733
|
+
]);
|
|
1734
|
+
|
|
1735
|
+
const AB = z.discriminatedUnion("status", [...A.options, ...B.options]);
|
|
1736
|
+
```
|
|
1737
|
+
|
|
1507
1738
|
## Records
|
|
1508
1739
|
|
|
1509
|
-
Record schemas are used to validate types such as `
|
|
1740
|
+
Record schemas are used to validate types such as `Record<string, number>`. This is particularly useful for storing or caching items by ID.
|
|
1510
1741
|
|
|
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)`:
|
|
1742
|
+
<!-- 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
1743
|
|
|
1513
1744
|
```ts
|
|
1514
1745
|
const NumberCache = z.record(z.number());
|
|
1515
1746
|
|
|
1516
1747
|
type NumberCache = z.infer<typeof NumberCache>;
|
|
1517
1748
|
// => { [k: string]: number }
|
|
1518
|
-
```
|
|
1519
|
-
|
|
1520
|
-
This is particularly useful for storing or caching items by ID.
|
|
1749
|
+
``` -->
|
|
1521
1750
|
|
|
1522
1751
|
```ts
|
|
1523
|
-
const
|
|
1524
|
-
|
|
1752
|
+
const User = z.object({ name: z.string() });
|
|
1753
|
+
|
|
1754
|
+
const UserStore = z.record(z.string(), User);
|
|
1755
|
+
type UserStore = z.infer<typeof UserStore>;
|
|
1756
|
+
// => Record<string, { name: string }>
|
|
1757
|
+
```
|
|
1525
1758
|
|
|
1526
|
-
type
|
|
1527
|
-
// => type UserStore = { [ x: string ]: { name: string } }
|
|
1759
|
+
The schema and inferred type can be used like so:
|
|
1528
1760
|
|
|
1761
|
+
```ts
|
|
1529
1762
|
const userStore: UserStore = {};
|
|
1530
1763
|
|
|
1531
1764
|
userStore["77d2586b-9e8e-4ecf-8b21-ea7e0530eadd"] = {
|
|
@@ -1537,19 +1770,6 @@ userStore["77d2586b-9e8e-4ecf-8b21-ea7e0530eadd"] = {
|
|
|
1537
1770
|
}; // TypeError
|
|
1538
1771
|
```
|
|
1539
1772
|
|
|
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
1773
|
**A note on numerical keys**
|
|
1554
1774
|
|
|
1555
1775
|
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.
|
|
@@ -1775,7 +1995,7 @@ const TestSchema = z.instanceof(Test);
|
|
|
1775
1995
|
|
|
1776
1996
|
const blob: any = "whatever";
|
|
1777
1997
|
TestSchema.parse(new Test()); // passes
|
|
1778
|
-
TestSchema.parse(
|
|
1998
|
+
TestSchema.parse(blob); // throws
|
|
1779
1999
|
```
|
|
1780
2000
|
|
|
1781
2001
|
## Functions
|
|
@@ -2576,36 +2796,63 @@ type inferred = z.infer<typeof stringToNumber>; // number
|
|
|
2576
2796
|
|
|
2577
2797
|
### Writing generic functions
|
|
2578
2798
|
|
|
2579
|
-
|
|
2799
|
+
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.
|
|
2800
|
+
|
|
2801
|
+
When attempting to write a function that accepts a Zod schema as an input, it's tempting to try something like this:
|
|
2580
2802
|
|
|
2581
2803
|
```ts
|
|
2582
|
-
function
|
|
2583
|
-
return schema
|
|
2804
|
+
function inferSchema<T>(schema: z.ZodType<T>) {
|
|
2805
|
+
return schema;
|
|
2584
2806
|
}
|
|
2585
2807
|
```
|
|
2586
2808
|
|
|
2587
|
-
This approach
|
|
2809
|
+
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
2810
|
|
|
2589
2811
|
```ts
|
|
2590
|
-
|
|
2591
|
-
|
|
2812
|
+
inferSchema(z.string());
|
|
2813
|
+
// => ZodType<string>
|
|
2592
2814
|
```
|
|
2593
2815
|
|
|
2594
|
-
|
|
2816
|
+
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`.
|
|
2817
|
+
|
|
2818
|
+
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
2819
|
|
|
2596
2820
|
```ts
|
|
2597
|
-
function
|
|
2598
|
-
return schema
|
|
2821
|
+
function inferSchema<T extends z.ZodTypeAny>(schema: T) {
|
|
2822
|
+
return schema;
|
|
2599
2823
|
}
|
|
2824
|
+
|
|
2825
|
+
inferSchema(z.string());
|
|
2826
|
+
// => ZodString
|
|
2600
2827
|
```
|
|
2601
2828
|
|
|
2602
2829
|
> `ZodTypeAny` is just a shorthand for `ZodType<any, any, any>`, a type that is broad enough to match any Zod schema.
|
|
2603
2830
|
|
|
2604
|
-
|
|
2831
|
+
The Result is now fully and properly typed, and the type system can infer the specific subclass of the schema.
|
|
2832
|
+
|
|
2833
|
+
#### Inferring the inferred type
|
|
2834
|
+
|
|
2835
|
+
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
2836
|
|
|
2606
2837
|
```ts
|
|
2607
|
-
|
|
2608
|
-
|
|
2838
|
+
function parseData<T extends z.ZodTypeAny>(data: unknown, schema: T) {
|
|
2839
|
+
return schema.parse(data);
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
parseData("sup", z.string());
|
|
2843
|
+
// => any
|
|
2844
|
+
```
|
|
2845
|
+
|
|
2846
|
+
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`.
|
|
2847
|
+
|
|
2848
|
+
```ts
|
|
2849
|
+
function parseData<T extends z.ZodTypeAny>(data: unknown, schema: T) {
|
|
2850
|
+
return schema.parse(data) as z.infer<T>;
|
|
2851
|
+
// ^^^^^^^^^^^^^^ <- add this
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
parseData("sup", z.string());
|
|
2855
|
+
// => string
|
|
2609
2856
|
```
|
|
2610
2857
|
|
|
2611
2858
|
#### Constraining allowable inputs
|