Capítulo 13 de 61
Zod has four distinct wrapper modifiers for "this value might be missing" — optional, exact-optional, nullable, and nullish — each mapping to a different TypeScript absence semantic.
z.optional(schema) / schema.optional(): allows the value to be undefined (key can be present-with-undefined or absent). Returns a ZodOptional; .unwrap() gets the inner schema back (Zod Mini: .def.innerType).z.exactOptional(schema) / schema.exactOptional(): allows the key to be absent, but not explicitly set to undefined — matches TypeScript's exactOptionalPropertyTypes semantics.z.nullable(schema) / schema.nullable(): allows the value to be null specifically (not undefined). Returns a ZodNullable, unwrappable the same way.z.nullish(schema): shorthand for optional and nullable combined — accepts undefined, null, or the underlying type.z.optional(z.literal("yoda")); // undefined | "yoda"
z.nullable(z.literal("yoda")); // null | "yoda"
z.nullish(z.literal("yoda")); // undefined | null | "yoda"
const User = z.object({ name: z.string().exactOptional() });
User.parse({}); // ok — key absent
User.parse({ name: "yoda" }); // ok
User.parse({ name: undefined }); // throws — key present but undefined
.optional() when you mean exactOptionalPropertyTypes semantics: .optional() accepts an explicit { name: undefined }; if your TS config has exactOptionalPropertyTypes: true and you want to match it exactly, use .exactOptional() instead.null and undefined are different values in JS — .nullable() alone does not accept a missing key or undefined.undefined (optional), null (nullable), either (nullish), or "key truly absent" (exact optional)..unwrap() (or .def.innerType in Zod Mini) recovers the original schema from any of these wrappers.exactOptional exists specifically to align with TypeScript's exactOptionalPropertyTypes compiler option — reach for it in codebases that enable that flag.