Zod 3.23 is now in beta! This is the final 3.x
release before Zod 4.0. To try it out:
npm install zod@beta
Features
z.string().date()
Zod can now validate ISO 8601 date strings. Thanks @igalklebanov! #1766
const schema = z.string().date();
schema.parse("2022-01-01"); // OK
z.string().time()
Zod can now validate ISO 8601 time strings. Thanks @igalklebanov! #1766
const schema = z.string().time();
schema.parse("12:00:00"); // OK
You can specify sub-second precision using the precision
option:
const schema = z.string().time({ precision: 3 });
schema.parse("12:00:00.123"); // OK
schema.parse("12:00:00.123456"); // Error
schema.parse("12:00:00"); // Error
z.string().duration()
Zod can now validate ISO 8601 duration strings. Thanks @mastermatt! #3265
const schema = z.string().duration();
schema.parse("P3Y6M4DT12H30M5S"); // OK
Improvements to z.string().datetime()
You can now allow unqualified (timezone-less) datetimes using the local: true
flag.
const schema = z.string().datetime({ local: true });
schema.parse("2022-01-01T12:00:00"); // OK
Plus, Zod now validates the day-of-month correctly to ensure no invalid dates (e.g. February 30th) pass validation. Thanks @szamanr! #3391
z.string().base64()
Zod can now validate base64 strings. Thanks @StefanTerdell! #3047
const schema = z.string().base64();
schema.parse("SGVsbG8gV29ybGQ="); // OK
Improved discriminated unions
The following can now be used as discriminator keys in z.discriminatedUnion()
:
ZodOptional
ZodNullable
ZodReadonly
ZodBranded
ZodCatch
const schema = z.discriminatedUnion("type", [
z.object({ type: z.literal("A").optional(), value: z.number() }),
z.object({ type: z.literal("B").nullable(), value: z.string() }),
z.object({ type: z.literal("C").readonly(), value: z.boolean() }),
z.object({ type: z.literal("D").readonly(), value: z.boolean() }),
z.object({ type: z.literal("E").catch("E"), value: z.unknown() }),
]);
Misc
- feature: allow falsy error message by @fernandollisboa in #3178
- feature: add attribute message to enum validatiion by @fernandollisboa in #3169
Breaking changes
There are no breaking changes to the public API of Zod. However some changes can impact ecosystem tools that rely on Zod internals.
ZodFirstPartySchemaTypes
Three new types have been added to the ZodFirstPartySchemaTypes
union. This may impact some codegen libraries. #3247
+ | ZodPipeline<any, any>
+ | ZodReadonly<any>
+ | ZodSymbol;
Default generics in ZodType
The third argument of the ZodType
base class now defaults to unknown
. This makes it easier to define recursive schemas and write generic functions that accept Zod schemas.
- class ZodType<Output = any, Def extends ZodTypeDef = ZodTypeDef, Input = Output> {}
+ class ZodType<Output = unknown, Def extends ZodTypeDef = ZodTypeDef, Input = unknown> {}
Unrecognized keys in .pick()
and .omit()
This version fixes a bug where unknown keys were accidentally accepted in .pick()
and omit()
. This has been fixed, which could cause compiler errors in some user code. #3255
z.object({
name: z.string()
}).pick({
notAKey: true // no longer allowed
})
Bugfixes and performance
- Bugfix: Enum.extract/exclude should not remove error mapping by @shaharke in #3240
- Added latest stable Node and TypeScript versions to test matrix for up-to-date testing. by @m10rten in #3278
- Add types to
ZodFirstPartySchemaTypes
by @MatthijsMud in #3247 - fix: make
input
of.required()
readonly by @KATT in #3301 - add never props to safe parse return types by @schicks in #3295
- Reporting errors of the preprocess that is the second property of object by @yukukotani in #2912
- Improve
addQuestionMarks
, fix #2184 by @colinhacks in #3352 - fix for njs by @dvv in #3063
- only look in
src
forbun test
by @rotu in #3038 - Restrict .pick()/.omit() mask type to only known properties by @petrovmiroslav in #3255
- Make EnumValues generic by @IlyaSemenov in #2338
- perf: avoid unnecessary error maps by @xuxucode in #2532
- Bugfix: z.record().parse should not filter out undefined values by @raik-casimiro in #3251
- Use Set.has instead of Array.indexOf for enum comparison (perf improvement) by @jmike in #2659
- [2888] fix emails with single quotes failing validation by @Mansehej in #2889
- Bugfix: Commas are incorrectly allowed in email regex. by @mokemoko in #3286
- Fix regex in cuid2 validation to be what cuid2 library expects by @etareduction in #2961
- Make depcruise pass by @rotu in #3037
- Faster ipv4 parsing by @colinhacks in #3413
Docs and ecosystem
- chore: add pastel package to ecosystem by @jlarmstrongiv in #2949
- added required styles. by @Ansh101112 in #2955
- Feature/better chinese translate by @NWYLZW in #2988
- Fix z.instanceof example by @alexnault in #3003
- Add documentation to Zod enum exclude/extract functions by @shaharke in #3044
- Add docs for coercing nullish values by @rbuetzer in #3067
- Adds
zod-dev
utility to eco-system section by @schalkventer in #3113 - Add zhttp library to docs by @evertdespiegeleer in #3134
- fixed Readme typo in NaNs example by @RashJrEdmund in #3181
- adds zod-config library to the ecosystem by @alexmarqs in #3200
- docs: update link and description of conform integration by @g1eny0ung in #3238
- Update README.md by @yugmade13 in #3317
- feat: overhaul generics section of readme to include more details on z.ZodTypeAny usage by @braden-w in #3321
- Fix small typos by @mmorearty in #3336
- docs: update Chinese docs and correct some of the typos by @jiechen257 in #3338
- docs: improve chinese readme by @luckrnx09 in #3371
- Add java-to-zod in X to Zod section by @ivangreene in #3385
- docs: add
orval
to "X to Zod" ecosystems by @soartec-lab in #3397
New Contributors
- @jlarmstrongiv made their first contribution in #2949
- @Ansh101112 made their first contribution in #2955
- @NWYLZW made their first contribution in #2988
- @alexnault made their first contribution in #3003
- @shaharke made their first contribution in #3044
- @rbuetzer made their first contribution in #3067
- @schalkventer made their first contribution in #3113
- @evertdespiegeleer made their first contribution in #3134
- @RashJrEdmund made their first contribution in #3181
- @alexmarqs made their first contribution in #3200
- @JonnyBurger made their first contribution in #3214
- @fernandollisboa made their first contribution in #3178
- @g1eny0ung made their first contribution in #3238
- @m10rten made their first contribution in #3278
- @MatthijsMud made their first contribution in #3247
- @yugmade13 made their first contribution in #3317
- @braden-w made their first contribution in #3321
- @mmorearty made their first contribution in #3336
- @schicks made their first contribution in #3295
- @yukukotani made their first contribution in #2912
- @jiechen257 made their first contribution in #3338
- @luckrnx09 made their first contribution in #3371
- @dvv made their first contribution in #3063
- @rotu made their first contribution in #3038
- @petrovmiroslav made their first contribution in #3255
- @ivoilic made their first contribution in #2364
- @telemakhos made their first contribution in #3388
- @bchrobot made their first contribution in #2522
- @szamanr made their first contribution in #3391
- @ivangreene made their first contribution in #3385
- @xuxucode made their first contribution in #2532
- @raik-casimiro made their first contribution in #3251
- @jmike made their first contribution in #2659
- @Mansehej made their first contribution in #2889
- @mokemoko made their first contribution in #3286
- @etareduction made their first contribution in #2961
- @mastermatt made their first contribution in #3265
- @soartec-lab made their first contribution in #3397
Full Changelog: v3.22.4...v3.23.0-beta