@zod-utils/core 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,79 +1,401 @@
1
1
  import * as z from 'zod';
2
2
 
3
3
  /**
4
- * Get the primitive type of a Zod field by unwrapping optional/nullable wrappers
5
- * @param field - The Zod field to unwrap
6
- * @param options - Options for unwrapping
7
- * @param options.unwrapArrays - If true, continues unwrapping arrays. If false (default), stops at arrays
8
- * @returns The unwrapped primitive type
9
- */
10
- declare const getPrimitiveType: <T extends z.ZodTypeAny>(field: T, options?: {
11
- unwrapArrays?: boolean;
12
- }) => z.ZodTypeAny;
13
- /**
14
- * Remove default values from a Zod field
15
- * @param field - The Zod field to remove defaults from
16
- * @returns The field without defaults
17
- */
18
- declare function removeDefault(field: z.ZodType): z.ZodType;
19
- /**
20
- * Check if a Zod field is required (not optional/nullable and doesn't accept empty values)
21
- * @param field - The Zod field to check
22
- * @returns True if the field is required
4
+ * Simplifies complex TypeScript types for better IDE hover tooltips and error messages.
5
+ *
6
+ * This utility type flattens intersections and complex type expressions into a single,
7
+ * readable object type. This is especially useful when working with mapped types,
8
+ * conditional types, or type intersections that produce hard-to-read IDE hints.
9
+ *
10
+ * @template T - The type to simplify
11
+ *
12
+ * @example
13
+ * Simplifying intersection types
14
+ * ```typescript
15
+ * type A = { name: string };
16
+ * type B = { age: number };
17
+ * type C = A & B; // Shows as "A & B" in IDE
18
+ * type D = Simplify<A & B>; // Shows as "{ name: string; age: number }" in IDE
19
+ * ```
20
+ *
21
+ * @example
22
+ * Simplifying Partial<> results
23
+ * ```typescript
24
+ * type User = { name: string; age: number; email: string };
25
+ * type PartialUser = Partial<User>; // Shows as "Partial<User>" in IDE
26
+ * type SimplifiedPartialUser = Simplify<Partial<User>>;
27
+ * // Shows as "{ name?: string; age?: number; email?: string }" in IDE
28
+ * ```
29
+ *
30
+ * @example
31
+ * Usage with zod schema inference
32
+ * ```typescript
33
+ * const schema = z.object({ id: z.string() })
34
+ * .merge(z.object({ name: z.string() }));
35
+ *
36
+ * type InferredType = z.infer<typeof schema>; // May show complex type
37
+ * type SimplifiedType = Simplify<z.infer<typeof schema>>;
38
+ * // Shows clear: { id: string; name: string }
39
+ * ```
40
+ *
41
+ * @since 0.1.0
23
42
  */
24
- declare const checkIfFieldIsRequired: <T extends z.ZodTypeAny>(field: T) => boolean;
43
+ type Simplify<T> = {
44
+ [K in keyof T]: T[K];
45
+ } & {};
25
46
 
26
47
  /**
27
- * Extract the default value from a Zod field (recursively unwraps optional/nullable)
48
+ * Extracts the default value from a Zod field, recursively unwrapping optional and nullable layers.
49
+ *
50
+ * This function traverses through wrapper types (like `ZodOptional`, `ZodNullable`) to find
51
+ * the underlying `ZodDefault` and returns its default value. If no default is found, returns `undefined`.
52
+ *
53
+ * @template T - The Zod type to extract default from
28
54
  * @param field - The Zod field to extract default from
29
55
  * @returns The default value if present, undefined otherwise
56
+ *
57
+ * @example
58
+ * Basic usage with default value
59
+ * ```typescript
60
+ * const field = z.string().default('hello');
61
+ * const defaultValue = extractDefault(field);
62
+ * // Result: 'hello'
63
+ * ```
64
+ *
65
+ * @example
66
+ * Unwrapping optional/nullable layers
67
+ * ```typescript
68
+ * const field = z.string().default('world').optional();
69
+ * const defaultValue = extractDefault(field);
70
+ * // Result: 'world' (unwraps optional to find default)
71
+ * ```
72
+ *
73
+ * @example
74
+ * Field without default
75
+ * ```typescript
76
+ * const field = z.string().optional();
77
+ * const defaultValue = extractDefault(field);
78
+ * // Result: undefined
79
+ * ```
80
+ *
81
+ * @see {@link getSchemaDefaults} for extracting defaults from entire schemas
82
+ * @since 0.1.0
30
83
  */
31
- declare function extractDefault(field: z.ZodTypeAny): any;
32
- /**
33
- * Get the unwrapped type without going through defaults
34
- * Useful for detecting nested objects/arrays while preserving defaults
35
- * @param field - The Zod field to unwrap
36
- * @returns The unwrapped type
37
- */
38
- declare function getUnwrappedType(field: z.ZodTypeAny): z.ZodTypeAny;
84
+ declare function extractDefault<T extends z.ZodTypeAny>(field: T): z.infer<T> | undefined;
39
85
  /**
40
- * Extract all default values from a Zod object schema
41
- * Recursively handles nested objects and only returns fields with defaults
86
+ * Extracts default values from a Zod object schema while skipping fields without defaults.
87
+ *
88
+ * This function recursively traverses the schema and collects all fields that have
89
+ * explicit default values defined. Fields without defaults are excluded from the result.
90
+ *
91
+ * **Important:** Nested defaults are NOT extracted unless the parent object also has
92
+ * an explicit `.default()`. This is by design to match Zod's default value behavior.
93
+ *
94
+ * @template T - The Zod object schema type
42
95
  * @param schema - The Zod object schema to extract defaults from
43
- * @returns Partial object with only fields that have defaults
96
+ * @returns A partial object containing only fields with default values
44
97
  *
45
98
  * @example
46
- * ```ts
99
+ * Basic usage
100
+ * ```typescript
47
101
  * const schema = z.object({
48
102
  * name: z.string().default('John'),
49
- * age: z.number(), // no default - skipped
103
+ * age: z.number(), // no default - will be skipped
104
+ * email: z.string().email().optional(),
105
+ * });
106
+ *
107
+ * const defaults = getSchemaDefaults(schema);
108
+ * // Result: { name: 'John' }
109
+ * ```
110
+ *
111
+ * @example
112
+ * Nested objects with defaults
113
+ * ```typescript
114
+ * const schema = z.object({
115
+ * user: z.object({
116
+ * name: z.string().default('Guest')
117
+ * }).default({ name: 'Guest' }), // ✅ Extracted because parent has .default()
118
+ *
50
119
  * settings: z.object({
51
120
  * theme: z.string().default('light')
52
- * })
121
+ * }), // ❌ NOT extracted - parent has no .default()
53
122
  * });
54
123
  *
55
- * getSchemaDefaults(schema);
56
- * // Returns: { name: 'John', settings: { theme: 'light' } }
124
+ * const defaults = getSchemaDefaults(schema);
125
+ * // Result: { user: { name: 'Guest' } }
57
126
  * ```
127
+ *
128
+ * @example
129
+ * Unwrapping optional/nullable fields
130
+ * ```typescript
131
+ * const schema = z.object({
132
+ * title: z.string().default('Untitled').optional(),
133
+ * count: z.number().default(0).nullable(),
134
+ * });
135
+ *
136
+ * const defaults = getSchemaDefaults(schema);
137
+ * // Result: { title: 'Untitled', count: 0 }
138
+ * ```
139
+ *
140
+ * @see {@link extractDefault} for extracting defaults from individual fields
141
+ * @since 0.1.0
58
142
  */
59
- declare function getSchemaDefaults<T extends z.ZodObject<any>>(schema: T): Partial<z.infer<T>>;
143
+ declare function getSchemaDefaults<T extends z.ZodObject>(schema: T): Simplify<Partial<z.infer<T>>>;
60
144
 
61
145
  /**
62
- * Extract the element type from an array or undefined
146
+ * Type representing a Zod type that has an unwrap method
63
147
  */
64
- type PickArrayObject<TArray extends unknown[] | undefined> = NonNullable<TArray>[number];
148
+ type Unwrappable = {
149
+ unwrap: () => z.ZodTypeAny;
150
+ };
65
151
  /**
66
- * Simplify complex types for better IDE hints
152
+ * Type guard to check if a Zod field can be unwrapped (has wrapper types like optional, nullable, default).
153
+ *
154
+ * This checks whether a Zod type has an `unwrap()` method, which is present on wrapper types
155
+ * like `ZodOptional`, `ZodNullable`, `ZodDefault`, and others.
156
+ *
157
+ * @param field - The Zod field to check
158
+ * @returns True if the field has an unwrap method, false otherwise
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * const optionalField = z.string().optional();
163
+ * console.log(canUnwrap(optionalField)); // true
164
+ *
165
+ * const plainField = z.string();
166
+ * console.log(canUnwrap(plainField)); // false
167
+ * ```
168
+ *
169
+ * @since 0.1.0
67
170
  */
68
- type Simplify<T> = {
69
- [K in keyof T]: T[K];
70
- } & {};
171
+ declare function canUnwrap(field: z.ZodTypeAny): field is z.ZodTypeAny & Unwrappable;
71
172
  /**
72
- * Make all properties optional and nullable
73
- * Useful for form input types where fields can be empty
173
+ * Unwraps a ZodUnion type and returns the first field and all union options.
174
+ *
175
+ * This function extracts the individual type options from a union type.
176
+ * By default, it filters out `ZodNull` and `ZodUndefined` types, returning only
177
+ * the meaningful type options. You can disable this filtering to get all options.
178
+ *
179
+ * @template T - The Zod type to unwrap
180
+ * @param field - The Zod field (union or single type)
181
+ * @param options - Configuration options
182
+ * @param options.filterNullish - Whether to filter out null and undefined types (default: true)
183
+ * @returns Object with `field` (first option) and `union` (all options array)
184
+ *
185
+ * @example
186
+ * Basic union unwrapping
187
+ * ```typescript
188
+ * const field = z.union([z.string(), z.number()]);
189
+ * const result = unwrapUnion(field);
190
+ * // Result: { field: z.string(), union: [z.string(), z.number()] }
191
+ * ```
192
+ *
193
+ * @example
194
+ * Union with null (filtered by default)
195
+ * ```typescript
196
+ * const field = z.union([z.string(), z.null()]);
197
+ * const result = unwrapUnion(field);
198
+ * // Result: { field: z.string(), union: [z.string()] }
199
+ * ```
200
+ *
201
+ * @example
202
+ * Union with null (keep all options)
203
+ * ```typescript
204
+ * const field = z.union([z.string(), z.null()]);
205
+ * const result = unwrapUnion(field, { filterNullish: false });
206
+ * // Result: { field: z.string(), union: [z.string(), z.null()] }
207
+ * ```
208
+ *
209
+ * @example
210
+ * Non-union type (returns single field)
211
+ * ```typescript
212
+ * const field = z.string();
213
+ * const result = unwrapUnion(field);
214
+ * // Result: { field: z.string(), union: [z.string()] }
215
+ * ```
216
+ *
217
+ * @example
218
+ * Nullable as union
219
+ * ```typescript
220
+ * const field = z.string().nullable(); // This is z.union([z.string(), z.null()])
221
+ * const result = unwrapUnion(field);
222
+ * // Result: { field: z.string(), union: [z.string()] } (null filtered out)
223
+ * ```
224
+ *
225
+ * @example
226
+ * Using the first field for type checking
227
+ * ```typescript
228
+ * const field = z.union([z.string(), z.number()]);
229
+ * const { field: firstField, union } = unwrapUnion(field);
230
+ * if (firstField instanceof z.ZodString) {
231
+ * console.log('First type is string');
232
+ * }
233
+ * ```
234
+ *
235
+ * @see {@link getPrimitiveType} for unwrapping wrapper types
236
+ * @since 0.1.0
74
237
  */
75
- type MakeOptionalAndNullable<T> = {
76
- [K in keyof T]?: T[K] | null;
238
+ declare function unwrapUnion<T extends z.ZodTypeAny>(field: T, options?: {
239
+ filterNullish?: boolean;
240
+ }): {
241
+ field: z.ZodTypeAny;
242
+ union: z.ZodTypeAny[];
77
243
  };
244
+ /**
245
+ * Gets the underlying primitive type of a Zod field by recursively unwrapping wrapper types.
246
+ *
247
+ * This function removes wrapper layers (optional, nullable, default) to reveal the base type.
248
+ * **Important:** It stops at array types without unwrapping them, treating arrays as primitives.
249
+ *
250
+ * @template T - The Zod type to unwrap
251
+ * @param field - The Zod field to unwrap
252
+ * @returns The unwrapped primitive Zod type
253
+ *
254
+ * @example
255
+ * Unwrapping to string primitive
256
+ * ```typescript
257
+ * const field = z.string().optional().nullable();
258
+ * const primitive = getPrimitiveType(field);
259
+ * // Result: z.string() (unwrapped all wrappers)
260
+ * ```
261
+ *
262
+ * @example
263
+ * Stopping at array type
264
+ * ```typescript
265
+ * const field = z.array(z.string()).optional();
266
+ * const primitive = getPrimitiveType(field);
267
+ * // Result: z.array(z.string()) (stops at array, doesn't unwrap it)
268
+ * ```
269
+ *
270
+ * @example
271
+ * Unwrapping defaults
272
+ * ```typescript
273
+ * const field = z.number().default(0).optional();
274
+ * const primitive = getPrimitiveType(field);
275
+ * // Result: z.number()
276
+ * ```
277
+ *
278
+ * @see {@link canUnwrap} for checking if a field can be unwrapped
279
+ * @since 0.1.0
280
+ */
281
+ declare const getPrimitiveType: <T extends z.ZodType>(field: T) => z.ZodTypeAny;
282
+ type StripZodDefault<T> = T extends z.ZodDefault<infer Inner> ? StripZodDefault<Inner> : T extends z.ZodOptional<infer Inner> ? z.ZodOptional<StripZodDefault<Inner>> : T extends z.ZodNullable<infer Inner> ? z.ZodNullable<StripZodDefault<Inner>> : T;
283
+ /**
284
+ * Removes default values from a Zod field while preserving other wrapper types.
285
+ *
286
+ * This function recursively removes `ZodDefault` wrappers from a field, while maintaining
287
+ * `optional()` and `nullable()` wrappers. Useful for scenarios where you want to check
288
+ * field requirements without considering default values.
289
+ *
290
+ * @template T - The Zod type to process
291
+ * @param field - The Zod field to remove defaults from
292
+ * @returns The field without defaults but with optional/nullable preserved
293
+ *
294
+ * @example
295
+ * Removing simple default
296
+ * ```typescript
297
+ * const field = z.string().default('hello');
298
+ * const withoutDefault = removeDefault(field);
299
+ * // Result: z.string()
300
+ * ```
301
+ *
302
+ * @example
303
+ * Preserving optional wrapper
304
+ * ```typescript
305
+ * const field = z.string().default('hello').optional();
306
+ * const withoutDefault = removeDefault(field);
307
+ * // Result: z.string().optional()
308
+ * ```
309
+ *
310
+ * @example
311
+ * Nested defaults
312
+ * ```typescript
313
+ * const field = z.string().default('inner').nullable().default('outer');
314
+ * const withoutDefault = removeDefault(field);
315
+ * // Result: z.string().nullable()
316
+ * ```
317
+ *
318
+ * @see {@link requiresValidInput} for usage with requirement checking
319
+ * @since 0.1.0
320
+ */
321
+ declare function removeDefault<T extends z.ZodType>(field: T): StripZodDefault<T>;
322
+ /**
323
+ * Determines if a field will show validation errors when the user submits empty or invalid input.
324
+ *
325
+ * This is useful for form UIs to indicate which fields require valid user input (e.g., showing
326
+ * asterisks, validation states). The key insight: **defaults are just initial values** - they
327
+ * don't prevent validation errors if the user clears the field.
328
+ *
329
+ * **Real-world example:**
330
+ * ```typescript
331
+ * // Marital status field with default but validation rules
332
+ * const maritalStatus = z.string().min(1).default('single');
333
+ *
334
+ * // Initial: field shows "single" (from default)
335
+ * // User deletes the value → field is now empty string
336
+ * // User submits form → validation fails because .min(1) rejects empty strings
337
+ * // requiresValidInput(maritalStatus) → true (shows * indicator, validation error)
338
+ * ```
339
+ *
340
+ * **How it works:**
341
+ * 1. Removes `.default()` wrappers (defaults are initial values, not validation rules)
342
+ * 2. Tests if the underlying schema accepts empty/invalid input:
343
+ * - `undefined` (via `.optional()`)
344
+ * - `null` (via `.nullable()`)
345
+ * - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)
346
+ * - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)
347
+ * 3. Returns `true` if validation will fail, `false` if empty input is accepted
348
+ *
349
+ * @template T - The Zod type to check
350
+ * @param field - The Zod field to check
351
+ * @returns True if the field will show validation errors on empty/invalid input, false otherwise
352
+ *
353
+ * @example
354
+ * User name field - required, no default
355
+ * ```typescript
356
+ * const userName = z.string().min(1);
357
+ * requiresValidInput(userName); // true - will error if user submits empty
358
+ * ```
359
+ *
360
+ * @example
361
+ * Marital status - required WITH default
362
+ * ```typescript
363
+ * const maritalStatus = z.string().min(1).default('single');
364
+ * requiresValidInput(maritalStatus); // true - will error if user clears and submits
365
+ * ```
366
+ *
367
+ * @example
368
+ * Age with default - requires valid input
369
+ * ```typescript
370
+ * const age = z.number().default(0);
371
+ * requiresValidInput(age); // true - numbers reject empty strings
372
+ * ```
373
+ *
374
+ * @example
375
+ * Optional bio field - doesn't require input
376
+ * ```typescript
377
+ * const bio = z.string().optional();
378
+ * requiresValidInput(bio); // false - user can leave empty
379
+ * ```
380
+ *
381
+ * @example
382
+ * String with default but NO validation - doesn't require input
383
+ * ```typescript
384
+ * const notes = z.string().default('N/A');
385
+ * requiresValidInput(notes); // false - plain z.string() accepts empty strings
386
+ * ```
387
+ *
388
+ * @example
389
+ * Nullable field - doesn't require input
390
+ * ```typescript
391
+ * const middleName = z.string().nullable();
392
+ * requiresValidInput(middleName); // false - user can leave null
393
+ * ```
394
+ *
395
+ * @see {@link removeDefault} for understanding how defaults are handled
396
+ * @see {@link getPrimitiveType} for understanding type unwrapping
397
+ * @since 0.1.0
398
+ */
399
+ declare const requiresValidInput: <T extends z.ZodType>(field: T) => boolean;
78
400
 
79
- export { type MakeOptionalAndNullable, type PickArrayObject, type Simplify, checkIfFieldIsRequired, extractDefault, getPrimitiveType, getSchemaDefaults, getUnwrappedType, removeDefault };
401
+ export { type Simplify, canUnwrap, extractDefault, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, unwrapUnion };