Immutable configuration
Expose validated configuration without allowing callers to mutate the parsed object.
Zod · Module
.readonly() marks a schema’s inferred output as readonly and freezes the parsed result at runtime. It supports objects, arrays, tuples, Set, and Map.
Calling .readonly() wraps a schema so its parsed output receives a readonly TypeScript type and is frozen at runtime with Object.freeze(). The input is validated by the underlying schema first, then the resulting object, array, tuple, Set, or Map is made non-modifiable.
schema.readonly() => readonly schemaA readonly schema whose parsed result is frozen.
.readonly() on a schema when its parsed output should be readonly.import { z } from "zod";
const ReadonlyUser = z.object({ name: z.string() }).readonly();
const user = ReadonlyUser.parse({ name: "Ada" });
console.log(user.name); // Ada
// user.name = "Grace"; // TypeScript error; the parsed object is frozen
Creates a readonly object schema, parses valid input, and returns a frozen result.
Expose validated configuration without allowing callers to mutate the parsed object.
Return validated collections whose TypeScript types and runtime values discourage modification.
.readonly() do?.readonly() returns a schema whose inferred output is marked readonly. It parses with the base schema and then freezes the result with Object.freeze().
Follow-up: Which output types does the documentation specifically call out? Objects, arrays, tuples, Set, and Map receive the readonly treatment.
For an object schema, the inferred output uses readonly properties, such as Readonly<{ name: string }>. At runtime, the parsed object is frozen to prevent modifications.
Follow-up: Does it validate before freezing? Yes; parsing uses the underlying schema first, and freezing is applied to the parsed result.
The method can be applied to arrays, tuples, maps, and sets as well as objects. The documentation shows outputs such as readonly string[], readonly [string, number], ReadonlyMap<string, Date>, and ReadonlySet<string>.
Follow-up: What happens to invalid input? The base schema still rejects it during normal parsing before any freezing occurs.
z.objectz.arrayz.tuplez.mapz.setObject.freeze