0
# Schema Wrappers
1
2
Modify schemas to make them optional, nullable, or provide default values.
3
4
## Optional & Nullable
5
6
```typescript { .api }
7
function optional<T extends ZodTypeAny>(schema: T): ZodOptional<T>;
8
function nullable<T extends ZodTypeAny>(schema: T): ZodNullable<T>;
9
function nullish<T extends ZodTypeAny>(schema: T): ZodOptional<ZodNullable<T>>;
10
```
11
12
**Examples:**
13
```typescript
14
z.string().optional() // string | undefined
15
z.string().nullable() // string | null
16
z.string().nullish() // string | null | undefined
17
18
// Or use directly
19
z.optional(z.string())
20
z.nullable(z.string())
21
z.nullish(z.string())
22
23
// In objects
24
z.object({
25
name: z.string(),
26
email: z.string().optional(),
27
phone: z.string().nullable(),
28
bio: z.string().nullish(),
29
})
30
```
31
32
## Default Values
33
34
```typescript { .api }
35
function _default<T extends ZodTypeAny>(
36
schema: T,
37
defaultValue: z.infer<T> | (() => z.infer<T>)
38
): ZodDefault<T>;
39
40
// Or use method
41
schema.default(defaultValue);
42
```
43
44
**Examples:**
45
```typescript
46
z.string().default("default value")
47
z.number().default(0)
48
z.boolean().default(false)
49
z.array(z.string()).default([])
50
51
// Function defaults (evaluated each time)
52
z.date().default(() => new Date())
53
z.string().default(() => generateId())
54
55
// In objects
56
z.object({
57
name: z.string(),
58
createdAt: z.date().default(() => new Date()),
59
status: z.enum(["pending", "active"]).default("pending"),
60
})
61
```
62
63
## Prefault
64
65
Default value applied before validation (at input level).
66
67
```typescript { .api }
68
function prefault<T extends ZodTypeAny>(
69
schema: T,
70
defaultValue: z.input<T> | (() => z.input<T>)
71
): ZodPrefault<T>;
72
```
73
74
**Examples:**
75
```typescript
76
// Apply default before transformation
77
z.string()
78
.prefault("")
79
.transform((s) => s.toUpperCase())
80
81
// Difference: default vs prefault
82
const DefaultSchema = z.string().transform((s) => s.toUpperCase()).default("hello");
83
// "hello" is applied after transform, so not uppercased
84
85
const PrefaultSchema = z.string().prefault("hello").transform((s) => s.toUpperCase());
86
// "hello" is applied before transform, so becomes "HELLO"
87
```
88
89
## Catch
90
91
Provide fallback value on validation error.
92
93
```typescript { .api }
94
function catch<T extends ZodTypeAny>(
95
schema: T,
96
fallback: z.infer<T> | ((ctx: { error: ZodError; input: unknown }) => z.infer<T>)
97
): ZodCatch<T>;
98
```
99
100
**Examples:**
101
```typescript
102
// Static fallback
103
z.string().catch("fallback")
104
z.number().catch(0)
105
z.array(z.string()).catch([])
106
107
// Function fallback
108
z.string().catch((ctx) => {
109
console.log("Error:", ctx.error);
110
console.log("Input:", ctx.input);
111
return "fallback";
112
})
113
114
// In objects
115
z.object({
116
count: z.number().catch(0),
117
items: z.array(z.string()).catch([]),
118
})
119
120
// Parsing with catch
121
const schema = z.string().catch("default");
122
schema.parse(123); // "default" (instead of throwing)
123
```
124
125
## Success
126
127
Always succeeds, returns input if valid, undefined if invalid.
128
129
```typescript { .api }
130
function success<T extends ZodTypeAny>(schema: T): ZodSuccess<T>;
131
```
132
133
**Examples:**
134
```typescript
135
z.string().success()
136
z.number().success()
137
138
const schema = z.string().success();
139
schema.parse("valid"); // "valid"
140
schema.parse(123); // undefined (no error thrown)
141
```
142
143
## Readonly
144
145
Makes schema output readonly (TypeScript only).
146
147
```typescript { .api }
148
function readonly<T extends ZodTypeAny>(schema: T): ZodReadonly<T>;
149
```
150
151
**Examples:**
152
```typescript
153
z.string().readonly()
154
z.array(z.string()).readonly() // readonly string[]
155
z.object({ name: z.string() }).readonly() // { readonly name: string }
156
157
// Read-only deep
158
const schema = z.object({
159
items: z.array(z.string()).readonly(),
160
}).readonly();
161
```
162
163
## Common Patterns
164
165
```typescript
166
// API response with defaults
167
const APIResponse = z.object({
168
data: z.array(z.any()).default([]),
169
page: z.number().default(1),
170
total: z.number().default(0),
171
});
172
173
// Form with optional fields
174
const FormSchema = z.object({
175
name: z.string(),
176
email: z.string().email(),
177
phone: z.string().optional(),
178
newsletter: z.boolean().default(false),
179
});
180
181
// Config with fallbacks
182
const ConfigSchema = z.object({
183
port: z.number().catch(3000),
184
host: z.string().catch("localhost"),
185
debug: z.boolean().catch(false),
186
});
187
188
// Optional with defaults
189
z.string().optional().default("value")
190
// type: string (always defined due to default)
191
192
// Nullable with defaults
193
z.string().nullable().default("value")
194
// type: string (always defined due to default)
195
196
// Catch with logging
197
const LoggedSchema = z.number().catch((ctx) => {
198
console.error("Validation failed:", ctx.error);
199
console.error("Input was:", ctx.input);
200
return 0;
201
});
202
203
// User profile schema
204
const UserProfile = z.object({
205
name: z.string(),
206
email: z.string().email(),
207
avatar: z.string().url().optional(),
208
bio: z.string().nullable(),
209
settings: z.object({
210
theme: z.enum(["light", "dark"]).default("light"),
211
notifications: z.boolean().default(true),
212
}).default({}),
213
});
214
```
215
216
## Wrapper Combinations
217
218
```typescript
219
// Optional with default
220
z.string().optional().default("value")
221
222
// Nullable with catch
223
z.string().nullable().catch("fallback")
224
225
// All together
226
z.string()
227
.optional()
228
.nullable()
229
.default("default")
230
.catch("fallback")
231
```
232