zod icon indicating copy to clipboard operation
zod copied to clipboard

Unable to create an object with readonly property

Open dmeehan1968 opened this issue 2 years ago • 4 comments

Possibly related to #728, but without the need for recursive application to the object properties.

z.object({
  id: z.string().readonly(),
  description: z.string(),
})

As of Zod 3.22.2, syntactically this is acceptable, but based on the description of readonly() it probably should not apply to primitive types). The resulting type drops the readonly and gives { id: string, description: string }.

I also tried an intersection of objects:

z.intersection(
  z.object({ id: z.string() }).readonly(),
  z.object({ description: z.string() })
)

But this oddly gives { id: any, description: string }, thus losing both the type and the readonly protection.

In my application, I have entities where the ID should not be changed (storage requires the old entity to be deleted and one with a new identity created). Zod is preventing me from creating a schema that enforces this.

The only way I can figure out how to set the id to be readonly is to cast the return type from parse, e.g:

schema.parse(input) as (z.infer<typeof schema> & { readonly deviceId: string })

or to create a type from the schema with a similar union:

type X = z.infer<typeof schema> & { readonly deviceId: string }

which seems like a total fudge.

dmeehan1968 avatar Sep 13 '23 07:09 dmeehan1968

Hey, can I take a look at this matter? I can see what's going on and hopefully find a solution to this problem

Isaac-alencar avatar Sep 18 '23 19:09 Isaac-alencar

@dmeehan1968 this sounds like an issue with Zod, but in the meantime a solution to this could be:

type ReadonlyProp<
  T extends Record<string, unknown>,
  Prop extends keyof T,
> = Readonly<Pick<T, Prop>> & Omit<T, Prop>

const schema = z
  .object({
    id: z.string().readonly(),
    description: z.string(),
  })
  .transform((values) => values as ReadonlyProp<typeof values, 'id'>)

type Output = z.infer<typeof schema>
/*
{
    readonly id: string;
    description: string;
}
*/

This way any typecasting stays within the schema

kamilwezgowiec avatar Oct 03 '23 13:10 kamilwezgowiec

The same issue

nixjs avatar Oct 09 '23 23:10 nixjs

I ran into a similar problem. I've submitted a solution as PR #3533 which allows for the props to be marked readonly and also to extend the resulting schema using the extend method. This allows for the creation of schemas with only some properties being readonly while also being generally extendable.

llowrey avatar May 29 '24 23:05 llowrey

Hi, @dmeehan1968. I'm Dosu, and I'm helping the Zod team manage their backlog. I'm marking this issue as stale.

Issue Summary:

  • You reported an issue with creating a Zod schema with a readonly property for primitive types using z.string().readonly().
  • @kamilwezgowiec suggested a workaround using a custom type transformation.
  • @llowrey submitted a pull request (#3533) to allow properties to be marked as readonly while enabling schema extension.
  • @Isaac-alencar expressed interest in further investigation.

Next Steps:

  • Please let me know if this issue is still relevant to the latest version of Zod. If so, you can keep the discussion open by commenting on the issue.
  • Otherwise, the issue will be automatically closed in 14 days.

Thank you for your understanding and contribution!

dosubot[bot] avatar Jul 24 '25 16:07 dosubot[bot]

What is the status of this issue. I am still facing this over a year later in a cloudflare worker, which as part of the response of a post endpoint has an id property that should not be present on the request.

nick4333 avatar Oct 04 '25 21:10 nick4333