zod
zod copied to clipboard
feat: Add `ZodReadonly` type
Overview
- Added support for the
ZodReadonly
type. - Added support for
deep readonly
as well. - Added option to toggle on/off the object freezing (
Object.freeze
) behavior
This type will transform the output
and input
of the following types to Readonly
:
- Array -> Adds the
readonly
modifier in front of it - Tuple -> Adds the
readonly
modifier in front of it - Map -> to the special
ReadonlyMap
type - Set -> to the special
ReadonlySet
type - Record -> Wraps it in
Readonly<Record< ... >>
- Object -> it adds the
readonly
modifier on each member - Native Enum -> doesn't make any difference because the parsing is not supposed to happen on the enum itself but in the enum values.
All other types, including Date
and Function
, which don't have support for Readonly
were left unchanged. Interestingly enough, TS can make Promises
readonly, but I've decided to skip them.
Deep readonly
The ZodReadonly
type has a method called deep()
that turns itself into a deep-readonly version. The behavior is basically the same, except that it recursively goes into Map
s, Set
s, objects
, records
, tuples
and arrays
and transforms everything to readonly
. It's important to note that it preserves tuples too.
Additionally,
This is not just a type improvement, but the ZodReadonly
can actually freeze the parsed data with Object.freeze
to reflect real "readonliness" (if in the deep version, it will recursively freeze the data). This is optional and you can opt-in via an option available in the createParams
.
The .readonly()
property was added to the root Z
class for convenience, but—again—only the aforementioned types get modified.
Deploy Preview for guileless-rolypoly-866f8a ready!
Built without sensitive environment variables
Name | Link |
---|---|
Latest commit | 2733ebebeb1b539c390608a0debeb587c4b3b7da |
Latest deploy log | https://app.netlify.com/sites/guileless-rolypoly-866f8a/deploys/63940ab47dac250008f0e024 |
Deploy Preview | https://deploy-preview-1432--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 settings.
I dig it. Is this still a draft or is it ready for review?
I dig it. Is this still a draft or is it ready for review?
@colinhacks Yes, I believe it's now ready for review.
Once we get this approved, I'm happy to add documentation around it in the README file prior to merging.
I'm not a maintainer, but I've been testing this out with our custom Express validator, and it's working fantastically. I particularly appreciate the runtime freeze support. Thanks for doing this!
I'm not a maintainer, but I've been testing this out with our custom Express validator, and it's working fantastically. I particularly appreciate the runtime freeze support. Thanks for doing this!
Thanks! We will get it merged hopefully soon.
Next step is a deep version.
Would be great to see this merged. Hate having to turn all my ReadonlyArray
s into Array
.
@colinhacks let me know if you need anything else from me.
Shall we move this forward?
@colinhacks / @mattieb / @izakfilmalter
I've introduced deep readonly
today. Feel free to review the PR one more time :)
Looking forward to helping deploy it.
@santosmarco PR is working well for me. Can't wait for it to get merged.
@colinhacks can it maybe receive some love for the next version?
Was looking exactly for this! Looking forward to see it added!
Looking forward to this as well.
Until then, there seem to be a few workarounds. Here's one using a type assertion:
import { z } from 'zod'
const thingsSchema = z.object({
things: z.array(z.string()) as z.ZodType<readonly string[]>
})
type Things = z.infer<typeof thingsSchema>
The type assertion can be codified into a polymorphic readonlyArray
convenience function that also has no overhead beyond just z.array
itself:
const readonlyArray: <T extends z.ZodTypeAny>(
schema: T
) => z.ZodType<readonly z.infer<T>[], z.ZodTypeDef, readonly unknown[]> =
z.array;
const thingsSchema = z.object({
things: readonlyArray(z.string())
})
type Things = z.infer<typeof thingsSchema>
My colleague @blackdynamo also proposed a runtime workaround using transform
and as const
:
import { z } from 'zod'
const thingsSchema = z.object({
things: z.array(z.string()).transform(things => [...things] as const)
})
type Things = z.infer<typeof thingsSchema>
I hope those are helpful to folks.
Any progress on this. Hate having to omit all the time to get readonly types.
I have strong empathy for the thankless work of maintaining a popular project, so this isn’t judgment—just practicality.
To solve my immediate problem, I have settled for using a deep readonly utility type (e.g. from type-fest) and applying it at the point I call Zod’s parse method. It’s a better solution, in my view, than trying to maintain a local fork or similar.
what exactly is blocking this?
What is the progress of this? Is there something necessary to be done? :)
oh so excited for this, i'm porting a big API from an in-house type assertion to zod, and having readonly is the sole blocking task.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
So it seems this has been implemented? Did this PR's code get used, or a different implementation? https://github.com/colinhacks/zod#readonly
@santosmarco-caribou
A modified version of this PR was merged in 3.22. Thanks @santosmarco-caribou!