@zod-utils/core 0.8.0 → 1.0.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/README.md +151 -0
- package/dist/index.d.mts +232 -66
- package/dist/index.d.ts +232 -66
- package/dist/index.js +92 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +91 -23
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -272,6 +272,157 @@ import { getFieldChecks, type ZodUnionCheck } from "@zod-utils/core";
|
|
|
272
272
|
|
|
273
273
|
---
|
|
274
274
|
|
|
275
|
+
### `extractDiscriminatedSchema(schema, key, value)`
|
|
276
|
+
|
|
277
|
+
Extract a specific variant from a discriminated union schema based on the discriminator field and value.
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import { extractDiscriminatedSchema } from "@zod-utils/core";
|
|
281
|
+
import { z } from "zod";
|
|
282
|
+
|
|
283
|
+
const userSchema = z.discriminatedUnion('mode', [
|
|
284
|
+
z.object({
|
|
285
|
+
mode: z.literal('create'),
|
|
286
|
+
name: z.string(),
|
|
287
|
+
age: z.number().optional(),
|
|
288
|
+
}),
|
|
289
|
+
z.object({
|
|
290
|
+
mode: z.literal('edit'),
|
|
291
|
+
id: z.number(),
|
|
292
|
+
name: z.string().optional(),
|
|
293
|
+
bio: z.string().optional(),
|
|
294
|
+
}),
|
|
295
|
+
]);
|
|
296
|
+
|
|
297
|
+
// Extract the 'create' variant
|
|
298
|
+
const createSchema = extractDiscriminatedSchema({
|
|
299
|
+
schema: userSchema,
|
|
300
|
+
key: 'mode',
|
|
301
|
+
value: 'create',
|
|
302
|
+
});
|
|
303
|
+
// Returns: z.ZodObject with { mode, name, age }
|
|
304
|
+
|
|
305
|
+
// Extract the 'edit' variant
|
|
306
|
+
const editSchema = extractDiscriminatedSchema({
|
|
307
|
+
schema: userSchema,
|
|
308
|
+
key: 'mode',
|
|
309
|
+
value: 'edit',
|
|
310
|
+
});
|
|
311
|
+
// Returns: z.ZodObject with { mode, id, name, bio }
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Use with discriminated unions:** This is essential when working with `z.discriminatedUnion()` schemas, as it extracts the correct variant schema based on the discriminator value.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### `extractFieldFromSchema(schema, fieldName, discriminator?)`
|
|
319
|
+
|
|
320
|
+
Extract a single field from a Zod object or discriminated union schema.
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
import { extractFieldFromSchema } from "@zod-utils/core";
|
|
324
|
+
import { z } from "zod";
|
|
325
|
+
|
|
326
|
+
// Simple object schema
|
|
327
|
+
const userSchema = z.object({
|
|
328
|
+
name: z.string(),
|
|
329
|
+
age: z.number(),
|
|
330
|
+
email: z.string().email(),
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const nameField = extractFieldFromSchema({
|
|
334
|
+
schema: userSchema,
|
|
335
|
+
fieldName: 'name',
|
|
336
|
+
});
|
|
337
|
+
// Returns: ZodString
|
|
338
|
+
|
|
339
|
+
// Discriminated union schema
|
|
340
|
+
const formSchema = z.discriminatedUnion('mode', [
|
|
341
|
+
z.object({
|
|
342
|
+
mode: z.literal('create'),
|
|
343
|
+
name: z.string(),
|
|
344
|
+
age: z.number().optional(),
|
|
345
|
+
}),
|
|
346
|
+
z.object({
|
|
347
|
+
mode: z.literal('edit'),
|
|
348
|
+
id: z.number(),
|
|
349
|
+
name: z.string().optional(),
|
|
350
|
+
}),
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
// Extract field from specific variant
|
|
354
|
+
const idField = extractFieldFromSchema({
|
|
355
|
+
schema: formSchema,
|
|
356
|
+
fieldName: 'id',
|
|
357
|
+
discriminator: {
|
|
358
|
+
key: 'mode',
|
|
359
|
+
value: 'edit',
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
// Returns: ZodNumber
|
|
363
|
+
|
|
364
|
+
// Without discriminator on discriminated union, returns undefined
|
|
365
|
+
const fieldWithoutDiscriminator = extractFieldFromSchema({
|
|
366
|
+
schema: formSchema,
|
|
367
|
+
fieldName: 'name',
|
|
368
|
+
});
|
|
369
|
+
// Returns: undefined (need discriminator to know which variant)
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**Discriminated union support:** When extracting fields from discriminated unions, you must provide the `discriminator` option with `key` and `value` to specify which variant to use.
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
### `getSchemaDefaults(schema, discriminator?)`
|
|
377
|
+
|
|
378
|
+
**Updated:** Now supports discriminated union schemas with the `discriminator` option.
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import { getSchemaDefaults } from "@zod-utils/core";
|
|
382
|
+
import { z } from "zod";
|
|
383
|
+
|
|
384
|
+
// Discriminated union with defaults
|
|
385
|
+
const formSchema = z.discriminatedUnion('mode', [
|
|
386
|
+
z.object({
|
|
387
|
+
mode: z.literal('create'),
|
|
388
|
+
name: z.string(),
|
|
389
|
+
age: z.number().default(18),
|
|
390
|
+
}),
|
|
391
|
+
z.object({
|
|
392
|
+
mode: z.literal('edit'),
|
|
393
|
+
id: z.number().default(1),
|
|
394
|
+
name: z.string().optional(),
|
|
395
|
+
bio: z.string().default('bio goes here'),
|
|
396
|
+
}),
|
|
397
|
+
]);
|
|
398
|
+
|
|
399
|
+
// Get defaults for 'create' mode
|
|
400
|
+
const createDefaults = getSchemaDefaults(formSchema, {
|
|
401
|
+
discriminator: {
|
|
402
|
+
key: 'mode',
|
|
403
|
+
value: 'create',
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
// Returns: { age: 18 }
|
|
407
|
+
|
|
408
|
+
// Get defaults for 'edit' mode
|
|
409
|
+
const editDefaults = getSchemaDefaults(formSchema, {
|
|
410
|
+
discriminator: {
|
|
411
|
+
key: 'mode',
|
|
412
|
+
value: 'edit',
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
// Returns: { id: 1, bio: 'bio goes here' }
|
|
416
|
+
|
|
417
|
+
// Without discriminator, returns empty object
|
|
418
|
+
const noDefaults = getSchemaDefaults(formSchema);
|
|
419
|
+
// Returns: {}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
**Discriminator types:** The discriminator `value` can be a string, number, or boolean literal that matches the discriminator field type.
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
275
426
|
## Type Utilities
|
|
276
427
|
|
|
277
428
|
### `Simplify<T>`
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as z from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import { util, z as z$1 } from 'zod';
|
|
3
|
+
import { $InferUnionOutput, $ZodCheckLessThanDef, $ZodCheckGreaterThanDef, $ZodCheckMultipleOfDef, $ZodCheckNumberFormatDef, $ZodCheckBigIntFormatDef, $ZodCheckMaxSizeDef, $ZodCheckMinSizeDef, $ZodCheckSizeEqualsDef, $ZodCheckMaxLengthDef, $ZodCheckMinLengthDef, $ZodCheckLengthEqualsDef, $ZodCheckStringFormatDef, $ZodCheckRegexDef, $ZodCheckLowerCaseDef, $ZodCheckUpperCaseDef, $ZodCheckIncludesDef, $ZodCheckStartsWithDef, $ZodCheckEndsWithDef, $ZodCheckPropertyDef, $ZodCheckMimeTypeDef, $ZodCheckOverwriteDef } from 'zod/v4/core';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Simplifies complex TypeScript types for better IDE hover tooltips and error messages.
|
|
@@ -51,8 +52,9 @@ type Simplify<T> = {
|
|
|
51
52
|
* This function traverses through wrapper types (like `ZodOptional`, `ZodNullable`, `ZodUnion`) to find
|
|
52
53
|
* the underlying `ZodDefault` and returns its default value. If no default is found, returns `undefined`.
|
|
53
54
|
*
|
|
54
|
-
* **Union handling:** For union types,
|
|
55
|
-
*
|
|
55
|
+
* **Union handling:** For union types, strips nullish types (null/undefined) first. If only one type
|
|
56
|
+
* remains after stripping, extracts the default from that type. If multiple non-nullish types remain,
|
|
57
|
+
* returns `undefined` (does not extract from any option).
|
|
56
58
|
*
|
|
57
59
|
* @template T - The Zod type to extract default from
|
|
58
60
|
* @param field - The Zod field to extract default from
|
|
@@ -75,19 +77,19 @@ type Simplify<T> = {
|
|
|
75
77
|
* ```
|
|
76
78
|
*
|
|
77
79
|
* @example
|
|
78
|
-
* Union with
|
|
80
|
+
* Union with only nullish types stripped to single type
|
|
79
81
|
* ```typescript
|
|
80
|
-
* const field = z.union([z.string().default('hello'), z.
|
|
82
|
+
* const field = z.union([z.string().default('hello'), z.null()]);
|
|
81
83
|
* const defaultValue = extractDefault(field);
|
|
82
|
-
* // Result: 'hello' (
|
|
84
|
+
* // Result: 'hello' (null stripped, leaving only string)
|
|
83
85
|
* ```
|
|
84
86
|
*
|
|
85
87
|
* @example
|
|
86
|
-
* Union with
|
|
88
|
+
* Union with multiple non-nullish types
|
|
87
89
|
* ```typescript
|
|
88
|
-
* const field = z.union([z.string(), z.number()
|
|
90
|
+
* const field = z.union([z.string().default('hello'), z.number()]);
|
|
89
91
|
* const defaultValue = extractDefault(field);
|
|
90
|
-
* // Result: undefined (
|
|
92
|
+
* // Result: undefined (multiple non-nullish types - no default extracted)
|
|
91
93
|
* ```
|
|
92
94
|
*
|
|
93
95
|
* @example
|
|
@@ -99,6 +101,7 @@ type Simplify<T> = {
|
|
|
99
101
|
* ```
|
|
100
102
|
*
|
|
101
103
|
* @see {@link getSchemaDefaults} for extracting defaults from entire schemas
|
|
104
|
+
* @see {@link tryStripNullishOnly} for union nullish stripping logic
|
|
102
105
|
* @since 0.1.0
|
|
103
106
|
*/
|
|
104
107
|
declare function extractDefault<T extends z.ZodTypeAny>(field: T): z.infer<T> | undefined;
|
|
@@ -114,8 +117,8 @@ declare function extractDefault<T extends z.ZodTypeAny>(field: T): z.infer<T> |
|
|
|
114
117
|
* **Component handling:** For form inputs without explicit defaults (like `z.string()` or `z.number()`),
|
|
115
118
|
* use the `?? ''` pattern in your components: `<Input value={field.value ?? ''} />`
|
|
116
119
|
*
|
|
117
|
-
* @template
|
|
118
|
-
* @param
|
|
120
|
+
* @template TSchema - The Zod object schema type
|
|
121
|
+
* @param targetSchema - The Zod object schema to extract defaults from
|
|
119
122
|
* @returns A partial object containing only fields with explicit default values
|
|
120
123
|
*
|
|
121
124
|
* @example
|
|
@@ -174,7 +177,21 @@ declare function extractDefault<T extends z.ZodTypeAny>(field: T): z.infer<T> |
|
|
|
174
177
|
* @see {@link extractDefault} for extracting defaults from individual fields
|
|
175
178
|
* @since 0.1.0
|
|
176
179
|
*/
|
|
177
|
-
declare function getSchemaDefaults<
|
|
180
|
+
declare function getSchemaDefaults<TSchema extends z.ZodObject | z.ZodDiscriminatedUnion, TDiscriminatorKey extends keyof z.infer<TSchema> & string, TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] & util.Literal>(schema: TSchema, options?: {
|
|
181
|
+
discriminator?: {
|
|
182
|
+
key: TDiscriminatorKey;
|
|
183
|
+
value: TDiscriminatorValue;
|
|
184
|
+
};
|
|
185
|
+
}): Simplify<Partial<z.infer<TSchema>>>;
|
|
186
|
+
|
|
187
|
+
declare function extractFieldFromSchema<TSchema extends z$1.ZodObject | z$1.ZodDiscriminatedUnion, TName extends keyof Extract<Required<z$1.infer<TSchema>>, Record<TDiscriminatorKey, TDiscriminatorValue>>, TDiscriminatorKey extends keyof z$1.infer<TSchema> & string, TDiscriminatorValue extends z$1.infer<TSchema>[TDiscriminatorKey] & util.Literal>({ schema, fieldName, discriminator, }: {
|
|
188
|
+
schema: TSchema;
|
|
189
|
+
fieldName: TName;
|
|
190
|
+
discriminator?: {
|
|
191
|
+
key: TDiscriminatorKey;
|
|
192
|
+
value: TDiscriminatorValue;
|
|
193
|
+
};
|
|
194
|
+
}): z$1.ZodType<unknown, unknown, z$1.core.$ZodTypeInternals<unknown, unknown>> | undefined;
|
|
178
195
|
|
|
179
196
|
/**
|
|
180
197
|
* Type representing a Zod type that has an unwrap method
|
|
@@ -204,83 +221,53 @@ type Unwrappable = {
|
|
|
204
221
|
*/
|
|
205
222
|
declare function canUnwrap(field: z.ZodTypeAny): field is z.ZodTypeAny & Unwrappable;
|
|
206
223
|
/**
|
|
207
|
-
*
|
|
224
|
+
* Attempts to strip nullish types from a union and return the single remaining type.
|
|
208
225
|
*
|
|
209
|
-
* This function
|
|
210
|
-
*
|
|
211
|
-
*
|
|
226
|
+
* This function filters out `ZodNull` and `ZodUndefined` from union types. If exactly
|
|
227
|
+
* one type remains after filtering, it returns that unwrapped type. Otherwise, it returns
|
|
228
|
+
* `false` to indicate the union couldn't be simplified to a single type.
|
|
212
229
|
*
|
|
213
|
-
* @
|
|
214
|
-
* @
|
|
215
|
-
* @param options - Configuration options
|
|
216
|
-
* @param options.filterNullish - Whether to filter out null and undefined types (default: true)
|
|
217
|
-
* @returns Object with `field` (first option) and `union` (all options array)
|
|
230
|
+
* @param field - The Zod field to process
|
|
231
|
+
* @returns The unwrapped type if only one remains, otherwise `false`
|
|
218
232
|
*
|
|
219
233
|
* @example
|
|
220
|
-
*
|
|
234
|
+
* Union with only nullish types filtered - returns single type
|
|
221
235
|
* ```typescript
|
|
222
|
-
* const field = z.union([z.string(), z.
|
|
223
|
-
* const result =
|
|
224
|
-
* // Result:
|
|
236
|
+
* const field = z.union([z.string(), z.null(), z.undefined()]);
|
|
237
|
+
* const result = tryStripNullishOnly(field);
|
|
238
|
+
* // Result: z.string() (unwrapped)
|
|
225
239
|
* ```
|
|
226
240
|
*
|
|
227
241
|
* @example
|
|
228
|
-
* Union with
|
|
242
|
+
* Union with multiple non-nullish types - returns false
|
|
229
243
|
* ```typescript
|
|
230
|
-
* const field = z.union([z.string(), z.
|
|
231
|
-
* const result =
|
|
232
|
-
* // Result:
|
|
233
|
-
* ```
|
|
234
|
-
*
|
|
235
|
-
* @example
|
|
236
|
-
* Union with null (keep all options)
|
|
237
|
-
* ```typescript
|
|
238
|
-
* const field = z.union([z.string(), z.null()]);
|
|
239
|
-
* const result = unwrapUnion(field, { filterNullish: false });
|
|
240
|
-
* // Result: { field: z.string(), union: [z.string(), z.null()] }
|
|
244
|
+
* const field = z.union([z.string(), z.number()]);
|
|
245
|
+
* const result = tryStripNullishOnly(field);
|
|
246
|
+
* // Result: false (cannot simplify to single type)
|
|
241
247
|
* ```
|
|
242
248
|
*
|
|
243
249
|
* @example
|
|
244
|
-
* Non-union type
|
|
250
|
+
* Non-union type - returns false
|
|
245
251
|
* ```typescript
|
|
246
252
|
* const field = z.string();
|
|
247
|
-
* const result =
|
|
248
|
-
* // Result:
|
|
249
|
-
* ```
|
|
250
|
-
*
|
|
251
|
-
* @example
|
|
252
|
-
* Nullable as union
|
|
253
|
-
* ```typescript
|
|
254
|
-
* const field = z.string().nullable(); // This is z.union([z.string(), z.null()])
|
|
255
|
-
* const result = unwrapUnion(field);
|
|
256
|
-
* // Result: { field: z.string(), union: [z.string()] } (null filtered out)
|
|
257
|
-
* ```
|
|
258
|
-
*
|
|
259
|
-
* @example
|
|
260
|
-
* Using the first field for type checking
|
|
261
|
-
* ```typescript
|
|
262
|
-
* const field = z.union([z.string(), z.number()]);
|
|
263
|
-
* const { field: firstField, union } = unwrapUnion(field);
|
|
264
|
-
* if (firstField instanceof z.ZodString) {
|
|
265
|
-
* console.log('First type is string');
|
|
266
|
-
* }
|
|
253
|
+
* const result = tryStripNullishOnly(field);
|
|
254
|
+
* // Result: false (not a union)
|
|
267
255
|
* ```
|
|
268
256
|
*
|
|
269
257
|
* @see {@link getPrimitiveType} for unwrapping wrapper types
|
|
270
|
-
* @since 0.
|
|
258
|
+
* @since 0.5.0
|
|
271
259
|
*/
|
|
272
|
-
declare function
|
|
273
|
-
filterNullish?: boolean;
|
|
274
|
-
}): {
|
|
275
|
-
field: z.ZodTypeAny;
|
|
276
|
-
union: z.ZodTypeAny[];
|
|
277
|
-
};
|
|
260
|
+
declare function tryStripNullishOnly(field: z.ZodTypeAny): z.ZodType | false;
|
|
278
261
|
/**
|
|
279
262
|
* Gets the underlying primitive type of a Zod field by recursively unwrapping wrapper types.
|
|
280
263
|
*
|
|
281
264
|
* This function removes wrapper layers (optional, nullable, default) to reveal the base type.
|
|
282
265
|
* **Important:** It stops at array types without unwrapping them, treating arrays as primitives.
|
|
283
266
|
*
|
|
267
|
+
* **Union handling:** For union types, strips nullish types (null/undefined) first. If only one
|
|
268
|
+
* type remains after stripping, unwraps to that type. If multiple non-nullish types remain,
|
|
269
|
+
* returns the union as-is (does not unwrap).
|
|
270
|
+
*
|
|
284
271
|
* @template T - The Zod type to unwrap
|
|
285
272
|
* @param field - The Zod field to unwrap
|
|
286
273
|
* @returns The unwrapped primitive Zod type
|
|
@@ -309,7 +296,24 @@ declare function unwrapUnion<T extends z.ZodTypeAny>(field: T, options?: {
|
|
|
309
296
|
* // Result: z.number()
|
|
310
297
|
* ```
|
|
311
298
|
*
|
|
299
|
+
* @example
|
|
300
|
+
* Union with only nullish types stripped to single type
|
|
301
|
+
* ```typescript
|
|
302
|
+
* const field = z.union([z.string(), z.null()]);
|
|
303
|
+
* const primitive = getPrimitiveType(field);
|
|
304
|
+
* // Result: z.string() (null stripped, leaving only string)
|
|
305
|
+
* ```
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* Union with multiple non-nullish types
|
|
309
|
+
* ```typescript
|
|
310
|
+
* const field = z.union([z.string(), z.number()]);
|
|
311
|
+
* const primitive = getPrimitiveType(field);
|
|
312
|
+
* // Result: z.union([z.string(), z.number()]) (returned as-is)
|
|
313
|
+
* ```
|
|
314
|
+
*
|
|
312
315
|
* @see {@link canUnwrap} for checking if a field can be unwrapped
|
|
316
|
+
* @see {@link tryStripNullishOnly} for union nullish stripping logic
|
|
313
317
|
* @since 0.1.0
|
|
314
318
|
*/
|
|
315
319
|
declare const getPrimitiveType: <T extends z.ZodType>(field: T) => z.ZodTypeAny;
|
|
@@ -521,5 +525,167 @@ type ZodUnionCheck = $ZodCheckLessThanDef | $ZodCheckGreaterThanDef | $ZodCheckM
|
|
|
521
525
|
* @since 0.4.0
|
|
522
526
|
*/
|
|
523
527
|
declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnionCheck>;
|
|
528
|
+
/**
|
|
529
|
+
* Recursively extracts the exact schema type from a discriminated union based on the discriminator value.
|
|
530
|
+
*
|
|
531
|
+
* This advanced TypeScript utility type walks through a union's options tuple at compile-time,
|
|
532
|
+
* checking each schema against the discriminator field and value, and returns the exact matching
|
|
533
|
+
* schema type (not a union of all options).
|
|
534
|
+
*
|
|
535
|
+
* **How it works:**
|
|
536
|
+
* 1. Extracts the options tuple from the union using `infer Options`
|
|
537
|
+
* 2. Destructure into head (`First`) and tail (`Rest`) using tuple pattern matching
|
|
538
|
+
* 3. Checks if `First` is a ZodObject with the matching discriminator field and value
|
|
539
|
+
* 4. If match found, returns `First` (the exact schema type)
|
|
540
|
+
* 5. If no match, recursively processes `Rest` until a match is found or list is exhausted
|
|
541
|
+
*
|
|
542
|
+
* **Type narrowing magic:**
|
|
543
|
+
* - Input: `z.discriminatedUnion('type', [SchemaA, SchemaB, SchemaC])`
|
|
544
|
+
* - Discriminator value: `'a'` (matches SchemaA)
|
|
545
|
+
* - Output: `SchemaA` (exact type, not `SchemaA | SchemaB | SchemaC`)
|
|
546
|
+
*
|
|
547
|
+
* @template TSchema - The ZodUnion or ZodDiscriminatedUnion type
|
|
548
|
+
* @template TDiscriminatorKey - The discriminator field name (e.g., "type", "mode")
|
|
549
|
+
* @template TDiscriminatorValue - The specific discriminator value (e.g., "create", "edit")
|
|
550
|
+
* @returns The exact matching schema type, or `never` if no match found
|
|
551
|
+
*
|
|
552
|
+
* @example
|
|
553
|
+
* ```typescript
|
|
554
|
+
* const schema = z.discriminatedUnion('mode', [
|
|
555
|
+
* z.object({ mode: z.literal('create'), name: z.string() }),
|
|
556
|
+
* z.object({ mode: z.literal('edit'), id: z.number() }),
|
|
557
|
+
* ]);
|
|
558
|
+
*
|
|
559
|
+
* // Exact type: z.object({ mode: z.literal('create'), name: z.string() })
|
|
560
|
+
* type CreateSchema = ExtractZodUnionMember<typeof schema, 'mode', 'create'>;
|
|
561
|
+
* ```
|
|
562
|
+
*/
|
|
563
|
+
type ExtractZodUnionMember<TSchema extends z.ZodUnion | z.ZodDiscriminatedUnion, TDiscriminatorKey extends keyof z.infer<TSchema> & string, TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] & util.Literal> = TSchema extends z.ZodUnion<infer Options> ? Options extends readonly [
|
|
564
|
+
infer First extends z.ZodTypeAny,
|
|
565
|
+
...infer Rest extends z.ZodTypeAny[]
|
|
566
|
+
] ? First extends z.ZodObject<infer Shape> ? TDiscriminatorKey extends keyof Shape ? Shape[TDiscriminatorKey] extends z.ZodLiteral<TDiscriminatorValue> ? First : Rest extends [] ? never : TDiscriminatorValue extends $InferUnionOutput<Rest[number]>[TDiscriminatorKey] ? ExtractZodUnionMember<z.ZodUnion<Rest>, TDiscriminatorKey, TDiscriminatorValue> : never : Rest extends [] ? never : TDiscriminatorValue extends $InferUnionOutput<Rest[number]>[TDiscriminatorKey] ? ExtractZodUnionMember<z.ZodUnion<Rest>, TDiscriminatorKey, TDiscriminatorValue> : never : never : never : never;
|
|
567
|
+
/**
|
|
568
|
+
* Extracts a specific schema option from a discriminated union based on the discriminator field value.
|
|
569
|
+
*
|
|
570
|
+
* This function finds and returns the **exact matching schema** from a `ZodDiscriminatedUnion` by
|
|
571
|
+
* comparing the discriminator field value. It's used internally by {@link getSchemaDefaults} to
|
|
572
|
+
* extract defaults from the correct schema variant in a discriminated union.
|
|
573
|
+
*
|
|
574
|
+
* **Key feature:** Returns the **exact schema type**, not a union of all options, thanks to the
|
|
575
|
+
* {@link ExtractZodUnionMember} recursive type utility. This enables precise type narrowing at
|
|
576
|
+
* compile-time based on the discriminator value.
|
|
577
|
+
*
|
|
578
|
+
* **How it works:**
|
|
579
|
+
* 1. Iterates through all options in the discriminated union at runtime
|
|
580
|
+
* 2. For each option, validates it's a ZodObject and checks if the discriminator field matches
|
|
581
|
+
* 3. Returns the first matching schema with its exact type narrowed at compile-time
|
|
582
|
+
* 4. Returns `undefined` if no match found or if option is not a ZodObject
|
|
583
|
+
*
|
|
584
|
+
* @template TSchema - The ZodUnion or ZodDiscriminatedUnion schema type
|
|
585
|
+
* @template TDiscriminatorKey - The discriminator field name (string key of the inferred union type)
|
|
586
|
+
* @template TDiscriminatorValue - The specific discriminator value to match (literal type)
|
|
587
|
+
* @param params - Parameters object
|
|
588
|
+
* @param params.schema - The discriminated union schema to search
|
|
589
|
+
* @param params.discriminatorKey - The discriminator field name (e.g., "mode", "type")
|
|
590
|
+
* @param params.discriminatorValue - The discriminator value to match (e.g., "create", "edit")
|
|
591
|
+
* @returns The exact matching schema option (with precise type), or `undefined` if not found
|
|
592
|
+
*
|
|
593
|
+
* @example
|
|
594
|
+
* Basic discriminated union - create/edit mode
|
|
595
|
+
* ```typescript
|
|
596
|
+
* const userSchema = z.discriminatedUnion('mode', [
|
|
597
|
+
* z.object({
|
|
598
|
+
* mode: z.literal('create'),
|
|
599
|
+
* name: z.string(),
|
|
600
|
+
* age: z.number().optional(),
|
|
601
|
+
* }),
|
|
602
|
+
* z.object({
|
|
603
|
+
* mode: z.literal('edit'),
|
|
604
|
+
* id: z.number(),
|
|
605
|
+
* name: z.string().optional(),
|
|
606
|
+
* }),
|
|
607
|
+
* ]);
|
|
608
|
+
*
|
|
609
|
+
* // Extract the "create" schema
|
|
610
|
+
* const createSchema = extractDiscriminatedSchema({
|
|
611
|
+
* schema: userSchema,
|
|
612
|
+
* discriminatorKey: 'mode',
|
|
613
|
+
* discriminatorValue: 'create',
|
|
614
|
+
* });
|
|
615
|
+
* // Result: z.object({ mode: z.literal('create'), name: z.string(), age: z.number().optional() })
|
|
616
|
+
*
|
|
617
|
+
* // Extract the "edit" schema
|
|
618
|
+
* const editSchema = extractDiscriminatedSchema({
|
|
619
|
+
* schema: userSchema,
|
|
620
|
+
* discriminatorKey: 'mode',
|
|
621
|
+
* discriminatorValue: 'edit',
|
|
622
|
+
* });
|
|
623
|
+
* // Result: z.object({ mode: z.literal('edit'), id: z.number(), name: z.string().optional() })
|
|
624
|
+
* ```
|
|
625
|
+
*
|
|
626
|
+
* @example
|
|
627
|
+
* Type-based discrimination
|
|
628
|
+
* ```typescript
|
|
629
|
+
* const eventSchema = z.discriminatedUnion('type', [
|
|
630
|
+
* z.object({ type: z.literal('click'), x: z.number(), y: z.number() }),
|
|
631
|
+
* z.object({ type: z.literal('keypress'), key: z.string() }),
|
|
632
|
+
* ]);
|
|
633
|
+
*
|
|
634
|
+
* const clickSchema = extractDiscriminatedSchema({
|
|
635
|
+
* schema: eventSchema,
|
|
636
|
+
* discriminatorKey: 'type',
|
|
637
|
+
* discriminatorValue: 'click',
|
|
638
|
+
* });
|
|
639
|
+
* // Result: z.object({ type: z.literal('click'), x: z.number(), y: z.number() })
|
|
640
|
+
* ```
|
|
641
|
+
*
|
|
642
|
+
* @example
|
|
643
|
+
* Invalid discriminator value
|
|
644
|
+
* ```typescript
|
|
645
|
+
* const schema = z.discriminatedUnion('mode', [
|
|
646
|
+
* z.object({ mode: z.literal('create'), name: z.string() }),
|
|
647
|
+
* ]);
|
|
648
|
+
*
|
|
649
|
+
* const result = extractDiscriminatedSchema({
|
|
650
|
+
* schema,
|
|
651
|
+
* discriminatorKey: 'mode',
|
|
652
|
+
* discriminatorValue: 'invalid', // doesn't match any option
|
|
653
|
+
* });
|
|
654
|
+
* // Result: undefined
|
|
655
|
+
* ```
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* Type narrowing demonstration
|
|
659
|
+
* ```typescript
|
|
660
|
+
* const schema = z.discriminatedUnion('mode', [
|
|
661
|
+
* z.object({ mode: z.literal('create'), name: z.string(), age: z.number() }),
|
|
662
|
+
* z.object({ mode: z.literal('edit'), id: z.number(), bio: z.string() }),
|
|
663
|
+
* ]);
|
|
664
|
+
*
|
|
665
|
+
* const createSchema = extractDiscriminatedSchema({
|
|
666
|
+
* schema,
|
|
667
|
+
* discriminatorKey: 'mode',
|
|
668
|
+
* discriminatorValue: 'create',
|
|
669
|
+
* });
|
|
670
|
+
*
|
|
671
|
+
* // Type is EXACTLY: z.object({ mode: z.literal('create'), name: z.string(), age: z.number() })
|
|
672
|
+
* // NOT: z.object({ mode: ..., ... }) | z.object({ mode: ..., ... }) | undefined
|
|
673
|
+
*
|
|
674
|
+
* if (createSchema) {
|
|
675
|
+
* createSchema.shape.age; // ✅ TypeScript knows 'age' exists
|
|
676
|
+
* createSchema.shape.name; // ✅ TypeScript knows 'name' exists
|
|
677
|
+
* // createSchema.shape.id; // ❌ TypeScript error: 'id' doesn't exist on 'create' schema
|
|
678
|
+
* }
|
|
679
|
+
* ```
|
|
680
|
+
*
|
|
681
|
+
* @see {@link getSchemaDefaults} for usage with discriminated unions
|
|
682
|
+
* @see {@link ExtractZodUnionMember} for the type-level extraction logic
|
|
683
|
+
* @since 0.6.0
|
|
684
|
+
*/
|
|
685
|
+
declare const extractDiscriminatedSchema: <TSchema extends z.ZodUnion | z.ZodDiscriminatedUnion, TDiscriminatorKey extends keyof z.infer<TSchema> & string, TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] & util.Literal>({ schema, key, value, }: {
|
|
686
|
+
schema: TSchema;
|
|
687
|
+
key: TDiscriminatorKey;
|
|
688
|
+
value: TDiscriminatorValue;
|
|
689
|
+
}) => ExtractZodUnionMember<TSchema, TDiscriminatorKey, TDiscriminatorValue> | undefined;
|
|
524
690
|
|
|
525
|
-
export { type Simplify, type ZodUnionCheck, canUnwrap, extractDefault, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput,
|
|
691
|
+
export { type Simplify, type ZodUnionCheck, canUnwrap, extractDefault, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };
|