@zod-utils/core 1.2.0 → 2.0.1

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 CHANGED
@@ -330,7 +330,7 @@ const editSchema = extractDiscriminatedSchema({
330
330
 
331
331
  ---
332
332
 
333
- ### `extractFieldFromSchema(schema, fieldName, discriminator?)`
333
+ ### `extractFieldFromSchema(schema, name, discriminator?)`
334
334
 
335
335
  Extract a single field from a Zod object or discriminated union schema.
336
336
 
@@ -349,7 +349,7 @@ const userSchema = z.object({
349
349
 
350
350
  const nameField = extractFieldFromSchema({
351
351
  schema: userSchema,
352
- fieldName: 'name',
352
+ name: 'name',
353
353
  });
354
354
  // Returns: ZodString
355
355
 
@@ -370,7 +370,7 @@ const formSchema = z.discriminatedUnion('mode', [
370
370
  // Extract field from specific variant
371
371
  const idField = extractFieldFromSchema({
372
372
  schema: formSchema,
373
- fieldName: 'id',
373
+ name: 'id',
374
374
  discriminator: {
375
375
  key: 'mode',
376
376
  value: 'edit',
@@ -381,7 +381,7 @@ const idField = extractFieldFromSchema({
381
381
  // Without discriminator on discriminated union, returns undefined
382
382
  const fieldWithoutDiscriminator = extractFieldFromSchema({
383
383
  schema: formSchema,
384
- fieldName: 'name',
384
+ name: 'name',
385
385
  });
386
386
  // Returns: undefined (need discriminator to know which variant)
387
387
 
@@ -395,7 +395,7 @@ const transformedSchema = z
395
395
 
396
396
  const nameFromTransformed = extractFieldFromSchema({
397
397
  schema: transformedSchema,
398
- fieldName: 'name',
398
+ name: 'name',
399
399
  });
400
400
  // Returns: ZodString (from the input type, not affected by transform)
401
401
  ```
@@ -404,6 +404,55 @@ const nameFromTransformed = extractFieldFromSchema({
404
404
 
405
405
  ---
406
406
 
407
+ ### `extendWithMeta(field, transform)`
408
+
409
+ Extends a Zod field with a transformation while preserving its metadata.
410
+
411
+ This is useful when you want to add validations or transformations to a field but keep the original metadata (like `translationKey`) intact.
412
+
413
+ ```typescript
414
+ import { extendWithMeta } from "@zod-utils/core";
415
+ import { z } from "zod";
416
+
417
+ // Base field with metadata
418
+ const baseField = z.string().meta({ translationKey: 'user.field.name' });
419
+
420
+ // Extend with validation while keeping metadata
421
+ const extendedField = extendWithMeta(baseField, (f) => f.min(3).max(100));
422
+
423
+ extendedField.meta(); // { translationKey: 'user.field.name' }
424
+
425
+ // Validation still works
426
+ extendedField.parse('ab'); // throws - too short
427
+ extendedField.parse('abc'); // 'abc' - valid
428
+ ```
429
+
430
+ **Use case:** When building forms with shared field definitions, you may want to reuse a base field with metadata across multiple schemas while adding schema-specific validations:
431
+
432
+ ```typescript
433
+ // Shared field definitions with i18n metadata
434
+ const fields = {
435
+ name: z.string().meta({ translationKey: 'user.field.name' }),
436
+ email: z.string().email().meta({ translationKey: 'user.field.email' }),
437
+ };
438
+
439
+ // Create form uses base fields with additional constraints
440
+ const createFormSchema = z.object({
441
+ name: extendWithMeta(fields.name, (f) => f.min(3).max(50)),
442
+ email: extendWithMeta(fields.email, (f) => f.min(5)),
443
+ });
444
+
445
+ // Edit form uses same fields with different constraints
446
+ const editFormSchema = z.object({
447
+ name: extendWithMeta(fields.name, (f) => f.optional()),
448
+ email: fields.email, // no extension needed
449
+ });
450
+ ```
451
+
452
+ **Note:** If the original field has no metadata, the transformed field is returned as-is without calling `.meta()`.
453
+
454
+ ---
455
+
407
456
  ### `getSchemaDefaults(schema, discriminator?)`
408
457
 
409
458
  **Updated:** Now supports discriminated union schemas with the `discriminator` option.
package/dist/index.d.mts CHANGED
@@ -77,6 +77,63 @@ type Discriminator<TSchema extends z$1.ZodType, TDiscriminatorKey extends Discri
77
77
  key: TDiscriminatorKey;
78
78
  value: TDiscriminatorValue;
79
79
  };
80
+ type Primitive = string | number | boolean | null | undefined | symbol | bigint;
81
+ type NonZeroDigit = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
82
+ type PathsHint<T, Prefix extends string = ''> = T extends Primitive ? never : T extends (infer E)[] ? (Prefix extends '' ? never : Prefix) | `${Prefix}.0` | PathsHint<E, `${Prefix}.0`> : T extends object ? {
83
+ [K in keyof T & string]: (Prefix extends '' ? K : `${Prefix}.${K}`) | PathsHint<T[K], Prefix extends '' ? K : `${Prefix}.${K}`>;
84
+ }[keyof T & string] : never;
85
+ type NonZeroIndex = `${NonZeroDigit}` | `${NonZeroDigit}${number}`;
86
+ type PathsLoose<T, Prefix extends string = ''> = T extends Primitive ? never : T extends (infer E)[] ? (Prefix extends '' ? never : Prefix) | `${Prefix}.${NonZeroIndex}` | PathsLoose<E, `${Prefix}.${NonZeroIndex}`> : T extends object ? {
87
+ [K in keyof T & string]: (Prefix extends '' ? K : `${Prefix}.${K}`) | PathsLoose<T[K], Prefix extends '' ? K : `${Prefix}.${K}`>;
88
+ }[keyof T & string] : never;
89
+ /**
90
+ * Generates all valid dot-notation paths for a given type.
91
+ * Supports nested objects and arrays with numeric indices.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * type User = { name: string; address: { city: string } };
96
+ * type UserPaths = Paths<User>;
97
+ * // "name" | "address" | "address.city"
98
+ * ```
99
+ */
100
+ type Paths<T> = PathsHint<T> | PathsLoose<T>;
101
+ /**
102
+ * Extracts fields common to all variants in a union type.
103
+ *
104
+ * Uses `Pick<T, keyof T>` to normalize union types by extracting only
105
+ * the keys that exist across all union members.
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * type A = { mode: 'create'; name: string };
110
+ * type B = { mode: 'edit'; id: number };
111
+ * type Common = CommonFields<A | B>;
112
+ * // { mode: 'create' | 'edit' }
113
+ * ```
114
+ */
115
+ type CommonFields<T> = Pick<T, keyof T>;
116
+ /**
117
+ * Generates valid dot-notation paths for fields in a discriminated union variant.
118
+ *
119
+ * Given a schema and discriminator value, extracts all possible field paths
120
+ * (including nested paths) for that specific variant.
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * const schema = z.discriminatedUnion('mode', [
125
+ * z.object({ mode: z.literal('create'), name: z.string() }),
126
+ * z.object({ mode: z.literal('edit'), id: z.number() }),
127
+ * ]);
128
+ *
129
+ * type CreatePaths = ValidPaths<typeof schema, 'mode', 'create'>;
130
+ * // "mode" | "name"
131
+ *
132
+ * type EditPaths = ValidPaths<typeof schema, 'mode', 'edit'>;
133
+ * // "mode" | "id"
134
+ * ```
135
+ */
136
+ type ValidPaths<TSchema extends z$1.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>> = Paths<CommonFields<Extract<Required<z$1.input<TSchema>>, TDiscriminatorKey extends never ? z$1.input<TSchema> : TDiscriminatorValue extends never ? z$1.input<TSchema> : Simplify<Record<TDiscriminatorKey, TDiscriminatorValue>>>>>;
80
137
 
81
138
  /**
82
139
  * Extracts the default value from a Zod field, recursively unwrapping optional, nullable, and union layers.
@@ -213,11 +270,200 @@ declare function getSchemaDefaults<TSchema extends z.ZodType, TDiscriminatorKey
213
270
  discriminator?: Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>;
214
271
  }): Simplify<Partial<z.input<TSchema>>>;
215
272
 
216
- declare function extractFieldFromSchema<TSchema extends z$1.ZodType, TName extends keyof Extract<Required<z$1.input<TSchema>>, Record<TDiscriminatorKey, TDiscriminatorValue>>, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>>({ schema, fieldName, discriminator, }: {
273
+ /**
274
+ * Recursively extracts the exact schema type from a discriminated union based on the discriminator value.
275
+ *
276
+ * This advanced TypeScript utility type walks through a union's options tuple at compile-time,
277
+ * checking each schema against the discriminator field and value, and returns the exact matching
278
+ * schema type (not a union of all options).
279
+ *
280
+ * **How it works:**
281
+ * 1. Extracts the options tuple from the union using `infer Options`
282
+ * 2. Destructure into head (`First`) and tail (`Rest`) using tuple pattern matching
283
+ * 3. Checks if `First` is a ZodObject with the matching discriminator field and value
284
+ * 4. If match found, returns `First` (the exact schema type)
285
+ * 5. If no match, recursively processes `Rest` until a match is found or list is exhausted
286
+ *
287
+ * **Type narrowing magic:**
288
+ * - Input: `z.discriminatedUnion('type', [SchemaA, SchemaB, SchemaC])`
289
+ * - Discriminator value: `'a'` (matches SchemaA)
290
+ * - Output: `SchemaA` (exact type, not `SchemaA | SchemaB | SchemaC`)
291
+ *
292
+ * @template TSchema - The ZodUnion or ZodDiscriminatedUnion type
293
+ * @template TDiscriminatorKey - The discriminator field name (e.g., "type", "mode")
294
+ * @template TDiscriminatorValue - The specific discriminator value (e.g., "create", "edit")
295
+ * @returns The exact matching schema type, or `never` if no match found
296
+ *
297
+ * @example
298
+ * ```typescript
299
+ * const schema = z.discriminatedUnion('mode', [
300
+ * z.object({ mode: z.literal('create'), name: z.string() }),
301
+ * z.object({ mode: z.literal('edit'), id: z.number() }),
302
+ * ]);
303
+ *
304
+ * // Exact type: z.object({ mode: z.literal('create'), name: z.string() })
305
+ * type CreateSchema = ExtractZodUnionMember<typeof schema, 'mode', 'create'>;
306
+ * ```
307
+ */
308
+ type ExtractZodUnionMember<TSchema extends z$1.ZodUnion | z$1.ZodDiscriminatedUnion, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends z$1.input<TSchema>[TDiscriminatorKey] & util.Literal> = TSchema extends z$1.ZodUnion<infer Options> ? Options extends readonly [
309
+ infer First extends z$1.ZodTypeAny,
310
+ ...infer Rest extends z$1.ZodTypeAny[]
311
+ ] ? First extends z$1.ZodObject<infer Shape> ? TDiscriminatorKey extends keyof Shape ? Shape[TDiscriminatorKey] extends z$1.ZodLiteral<TDiscriminatorValue> | z$1.ZodDefault<z$1.ZodLiteral<TDiscriminatorValue>> ? First : Rest extends [] ? never : TDiscriminatorValue extends $InferUnionInput<Rest[number]>[TDiscriminatorKey] ? ExtractZodUnionMember<z$1.ZodUnion<Rest>, TDiscriminatorKey, TDiscriminatorValue> : never : Rest extends [] ? never : TDiscriminatorValue extends $InferUnionInput<Rest[number]>[TDiscriminatorKey] ? ExtractZodUnionMember<z$1.ZodUnion<Rest>, TDiscriminatorKey, TDiscriminatorValue> : never : never : never : never;
312
+ /**
313
+ * Extracts a specific schema option from a discriminated union based on the discriminator field value.
314
+ *
315
+ * This function finds and returns the **exact matching schema** from a `ZodDiscriminatedUnion` by
316
+ * comparing the discriminator field value. It's used internally by {@link getSchemaDefaults} to
317
+ * extract defaults from the correct schema variant in a discriminated union.
318
+ *
319
+ * **Key feature:** Returns the **exact schema type**, not a union of all options, thanks to the
320
+ * {@link ExtractZodUnionMember} recursive type utility. This enables precise type narrowing at
321
+ * compile-time based on the discriminator value.
322
+ *
323
+ * **How it works:**
324
+ * 1. Iterates through all options in the discriminated union at runtime
325
+ * 2. For each option, validates it's a ZodObject and checks if the discriminator field matches
326
+ * 3. Returns the first matching schema with its exact type narrowed at compile-time
327
+ * 4. Returns `undefined` if no match found or if option is not a ZodObject
328
+ *
329
+ * @template TSchema - The ZodUnion or ZodDiscriminatedUnion schema type
330
+ * @template TDiscriminatorKey - The discriminator field name (string key of the inferred union type)
331
+ * @template TDiscriminatorValue - The specific discriminator value to match (literal type)
332
+ * @param params - Parameters object
333
+ * @param params.schema - The discriminated union schema to search
334
+ * @param params.discriminatorKey - The discriminator field name (e.g., "mode", "type")
335
+ * @param params.discriminatorValue - The discriminator value to match (e.g., "create", "edit")
336
+ * @returns The exact matching schema option (with precise type), or `undefined` if not found
337
+ *
338
+ * @example
339
+ * Basic discriminated union - create/edit mode
340
+ * ```typescript
341
+ * const userSchema = z.discriminatedUnion('mode', [
342
+ * z.object({
343
+ * mode: z.literal('create'),
344
+ * name: z.string(),
345
+ * age: z.number().optional(),
346
+ * }),
347
+ * z.object({
348
+ * mode: z.literal('edit'),
349
+ * id: z.number(),
350
+ * name: z.string().optional(),
351
+ * }),
352
+ * ]);
353
+ *
354
+ * // Extract the "create" schema
355
+ * const createSchema = extractDiscriminatedSchema({
356
+ * schema: userSchema,
357
+ * discriminatorKey: 'mode',
358
+ * discriminatorValue: 'create',
359
+ * });
360
+ * // Result: z.object({ mode: z.literal('create'), name: z.string(), age: z.number().optional() })
361
+ *
362
+ * // Extract the "edit" schema
363
+ * const editSchema = extractDiscriminatedSchema({
364
+ * schema: userSchema,
365
+ * discriminatorKey: 'mode',
366
+ * discriminatorValue: 'edit',
367
+ * });
368
+ * // Result: z.object({ mode: z.literal('edit'), id: z.number(), name: z.string().optional() })
369
+ * ```
370
+ *
371
+ * @example
372
+ * Type-based discrimination
373
+ * ```typescript
374
+ * const eventSchema = z.discriminatedUnion('type', [
375
+ * z.object({ type: z.literal('click'), x: z.number(), y: z.number() }),
376
+ * z.object({ type: z.literal('keypress'), key: z.string() }),
377
+ * ]);
378
+ *
379
+ * const clickSchema = extractDiscriminatedSchema({
380
+ * schema: eventSchema,
381
+ * discriminatorKey: 'type',
382
+ * discriminatorValue: 'click',
383
+ * });
384
+ * // Result: z.object({ type: z.literal('click'), x: z.number(), y: z.number() })
385
+ * ```
386
+ *
387
+ * @example
388
+ * Invalid discriminator value
389
+ * ```typescript
390
+ * const schema = z.discriminatedUnion('mode', [
391
+ * z.object({ mode: z.literal('create'), name: z.string() }),
392
+ * ]);
393
+ *
394
+ * const result = extractDiscriminatedSchema({
395
+ * schema,
396
+ * discriminatorKey: 'mode',
397
+ * discriminatorValue: 'invalid', // doesn't match any option
398
+ * });
399
+ * // Result: undefined
400
+ * ```
401
+ *
402
+ * @example
403
+ * Type narrowing demonstration
404
+ * ```typescript
405
+ * const schema = z.discriminatedUnion('mode', [
406
+ * z.object({ mode: z.literal('create'), name: z.string(), age: z.number() }),
407
+ * z.object({ mode: z.literal('edit'), id: z.number(), bio: z.string() }),
408
+ * ]);
409
+ *
410
+ * const createSchema = extractDiscriminatedSchema({
411
+ * schema,
412
+ * discriminatorKey: 'mode',
413
+ * discriminatorValue: 'create',
414
+ * });
415
+ *
416
+ * // Type is EXACTLY: z.object({ mode: z.literal('create'), name: z.string(), age: z.number() })
417
+ * // NOT: z.object({ mode: ..., ... }) | z.object({ mode: ..., ... }) | undefined
418
+ *
419
+ * if (createSchema) {
420
+ * createSchema.shape.age; // ✅ TypeScript knows 'age' exists
421
+ * createSchema.shape.name; // ✅ TypeScript knows 'name' exists
422
+ * // createSchema.shape.id; // ❌ TypeScript error: 'id' doesn't exist on 'create' schema
423
+ * }
424
+ * ```
425
+ *
426
+ * @see {@link getSchemaDefaults} for usage with discriminated unions
427
+ * @see {@link ExtractZodUnionMember} for the type-level extraction logic
428
+ * @since 0.6.0
429
+ */
430
+ declare const extractDiscriminatedSchema: <TSchema extends z$1.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>, ReturnType extends TSchema extends z$1.ZodDiscriminatedUnion ? ExtractZodUnionMember<TSchema, TDiscriminatorKey, TDiscriminatorValue> : never>({ schema, key, value, }: {
431
+ schema: TSchema;
432
+ } & Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>) => ReturnType;
433
+
434
+ type Split<S extends string> = S extends `${infer Head}.${infer Tail}` ? [Head, ...Split<Tail>] : [S];
435
+ type IsNumeric<S extends string> = S extends `${number}` ? true : false;
436
+ type Unwrap<T> = T extends z$1.ZodOptional<infer U> ? Unwrap<U> : T extends z$1.ZodNullable<infer U> ? Unwrap<U> : T extends z$1.ZodDefault<infer U> ? Unwrap<U> : T;
437
+ type NavigateZod<T, Path extends string[]> = Path extends [
438
+ infer First extends string,
439
+ ...infer Rest extends string[]
440
+ ] ? Unwrap<T> extends z$1.ZodObject<infer Shape> ? First extends keyof Shape ? Rest extends [] ? Shape[First] : NavigateZod<Shape[First], Rest> : never : Unwrap<T> extends z$1.ZodArray<infer Element> ? IsNumeric<First> extends true ? Rest extends [] ? Element : NavigateZod<Element, Rest> : never : never : T;
441
+ type ExtractZodByPath<Schema, Path extends string> = NavigateZod<Schema, Split<Path>>;
442
+ declare function extractFieldFromSchema<TSchema extends z$1.ZodType, TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue>, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>>({ schema, name, discriminator, }: {
217
443
  schema: TSchema;
218
- fieldName: TName;
444
+ name: TPath;
219
445
  discriminator?: Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>;
220
- }): z$1.ZodType<unknown, unknown, z$1.core.$ZodTypeInternals<unknown, unknown>> | undefined;
446
+ }): (ExtractZodByPath<TSchema, TPath> & z$1.ZodType) | undefined;
447
+ /**
448
+ * Extends a Zod field with a transformation while preserving its metadata.
449
+ *
450
+ * This is useful when you want to add validations or transformations to a field
451
+ * but keep the original metadata (like translationKey) intact.
452
+ *
453
+ * @param field - The original Zod field
454
+ * @param transform - A function that transforms the field
455
+ * @returns The transformed field with preserved metadata
456
+ *
457
+ * @example
458
+ * ```typescript
459
+ * const baseField = z.string().meta({ translationKey: 'user.field.name' });
460
+ *
461
+ * // Add min/max validation while keeping the translationKey
462
+ * const extendedField = extendWithMeta(baseField, (f) => f.min(3).max(100));
463
+ * extendedField.meta(); // { translationKey: 'user.field.name' }
464
+ * ```
465
+ */
466
+ declare function extendWithMeta<T extends z$1.ZodType, R extends z$1.ZodType>(field: T, transform: (f: T) => R): R;
221
467
 
222
468
  /**
223
469
  * Type representing a Zod type that has an unwrap method
@@ -551,165 +797,5 @@ type ZodUnionCheck = $ZodCheckLessThanDef | $ZodCheckGreaterThanDef | $ZodCheckM
551
797
  * @since 0.4.0
552
798
  */
553
799
  declare function getFieldChecks<T extends z$1.ZodTypeAny>(field: T): Array<ZodUnionCheck>;
554
- /**
555
- * Recursively extracts the exact schema type from a discriminated union based on the discriminator value.
556
- *
557
- * This advanced TypeScript utility type walks through a union's options tuple at compile-time,
558
- * checking each schema against the discriminator field and value, and returns the exact matching
559
- * schema type (not a union of all options).
560
- *
561
- * **How it works:**
562
- * 1. Extracts the options tuple from the union using `infer Options`
563
- * 2. Destructure into head (`First`) and tail (`Rest`) using tuple pattern matching
564
- * 3. Checks if `First` is a ZodObject with the matching discriminator field and value
565
- * 4. If match found, returns `First` (the exact schema type)
566
- * 5. If no match, recursively processes `Rest` until a match is found or list is exhausted
567
- *
568
- * **Type narrowing magic:**
569
- * - Input: `z.discriminatedUnion('type', [SchemaA, SchemaB, SchemaC])`
570
- * - Discriminator value: `'a'` (matches SchemaA)
571
- * - Output: `SchemaA` (exact type, not `SchemaA | SchemaB | SchemaC`)
572
- *
573
- * @template TSchema - The ZodUnion or ZodDiscriminatedUnion type
574
- * @template TDiscriminatorKey - The discriminator field name (e.g., "type", "mode")
575
- * @template TDiscriminatorValue - The specific discriminator value (e.g., "create", "edit")
576
- * @returns The exact matching schema type, or `never` if no match found
577
- *
578
- * @example
579
- * ```typescript
580
- * const schema = z.discriminatedUnion('mode', [
581
- * z.object({ mode: z.literal('create'), name: z.string() }),
582
- * z.object({ mode: z.literal('edit'), id: z.number() }),
583
- * ]);
584
- *
585
- * // Exact type: z.object({ mode: z.literal('create'), name: z.string() })
586
- * type CreateSchema = ExtractZodUnionMember<typeof schema, 'mode', 'create'>;
587
- * ```
588
- */
589
- type ExtractZodUnionMember<TSchema extends z$1.ZodUnion | z$1.ZodDiscriminatedUnion, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends z$1.input<TSchema>[TDiscriminatorKey] & util.Literal> = TSchema extends z$1.ZodUnion<infer Options> ? Options extends readonly [
590
- infer First extends z$1.ZodTypeAny,
591
- ...infer Rest extends z$1.ZodTypeAny[]
592
- ] ? First extends z$1.ZodObject<infer Shape> ? TDiscriminatorKey extends keyof Shape ? Shape[TDiscriminatorKey] extends z$1.ZodLiteral<TDiscriminatorValue> | z$1.ZodDefault<z$1.ZodLiteral<TDiscriminatorValue>> ? First : Rest extends [] ? never : TDiscriminatorValue extends $InferUnionInput<Rest[number]>[TDiscriminatorKey] ? ExtractZodUnionMember<z$1.ZodUnion<Rest>, TDiscriminatorKey, TDiscriminatorValue> : never : Rest extends [] ? never : TDiscriminatorValue extends $InferUnionInput<Rest[number]>[TDiscriminatorKey] ? ExtractZodUnionMember<z$1.ZodUnion<Rest>, TDiscriminatorKey, TDiscriminatorValue> : never : never : never : never;
593
- /**
594
- * Extracts a specific schema option from a discriminated union based on the discriminator field value.
595
- *
596
- * This function finds and returns the **exact matching schema** from a `ZodDiscriminatedUnion` by
597
- * comparing the discriminator field value. It's used internally by {@link getSchemaDefaults} to
598
- * extract defaults from the correct schema variant in a discriminated union.
599
- *
600
- * **Key feature:** Returns the **exact schema type**, not a union of all options, thanks to the
601
- * {@link ExtractZodUnionMember} recursive type utility. This enables precise type narrowing at
602
- * compile-time based on the discriminator value.
603
- *
604
- * **How it works:**
605
- * 1. Iterates through all options in the discriminated union at runtime
606
- * 2. For each option, validates it's a ZodObject and checks if the discriminator field matches
607
- * 3. Returns the first matching schema with its exact type narrowed at compile-time
608
- * 4. Returns `undefined` if no match found or if option is not a ZodObject
609
- *
610
- * @template TSchema - The ZodUnion or ZodDiscriminatedUnion schema type
611
- * @template TDiscriminatorKey - The discriminator field name (string key of the inferred union type)
612
- * @template TDiscriminatorValue - The specific discriminator value to match (literal type)
613
- * @param params - Parameters object
614
- * @param params.schema - The discriminated union schema to search
615
- * @param params.discriminatorKey - The discriminator field name (e.g., "mode", "type")
616
- * @param params.discriminatorValue - The discriminator value to match (e.g., "create", "edit")
617
- * @returns The exact matching schema option (with precise type), or `undefined` if not found
618
- *
619
- * @example
620
- * Basic discriminated union - create/edit mode
621
- * ```typescript
622
- * const userSchema = z.discriminatedUnion('mode', [
623
- * z.object({
624
- * mode: z.literal('create'),
625
- * name: z.string(),
626
- * age: z.number().optional(),
627
- * }),
628
- * z.object({
629
- * mode: z.literal('edit'),
630
- * id: z.number(),
631
- * name: z.string().optional(),
632
- * }),
633
- * ]);
634
- *
635
- * // Extract the "create" schema
636
- * const createSchema = extractDiscriminatedSchema({
637
- * schema: userSchema,
638
- * discriminatorKey: 'mode',
639
- * discriminatorValue: 'create',
640
- * });
641
- * // Result: z.object({ mode: z.literal('create'), name: z.string(), age: z.number().optional() })
642
- *
643
- * // Extract the "edit" schema
644
- * const editSchema = extractDiscriminatedSchema({
645
- * schema: userSchema,
646
- * discriminatorKey: 'mode',
647
- * discriminatorValue: 'edit',
648
- * });
649
- * // Result: z.object({ mode: z.literal('edit'), id: z.number(), name: z.string().optional() })
650
- * ```
651
- *
652
- * @example
653
- * Type-based discrimination
654
- * ```typescript
655
- * const eventSchema = z.discriminatedUnion('type', [
656
- * z.object({ type: z.literal('click'), x: z.number(), y: z.number() }),
657
- * z.object({ type: z.literal('keypress'), key: z.string() }),
658
- * ]);
659
- *
660
- * const clickSchema = extractDiscriminatedSchema({
661
- * schema: eventSchema,
662
- * discriminatorKey: 'type',
663
- * discriminatorValue: 'click',
664
- * });
665
- * // Result: z.object({ type: z.literal('click'), x: z.number(), y: z.number() })
666
- * ```
667
- *
668
- * @example
669
- * Invalid discriminator value
670
- * ```typescript
671
- * const schema = z.discriminatedUnion('mode', [
672
- * z.object({ mode: z.literal('create'), name: z.string() }),
673
- * ]);
674
- *
675
- * const result = extractDiscriminatedSchema({
676
- * schema,
677
- * discriminatorKey: 'mode',
678
- * discriminatorValue: 'invalid', // doesn't match any option
679
- * });
680
- * // Result: undefined
681
- * ```
682
- *
683
- * @example
684
- * Type narrowing demonstration
685
- * ```typescript
686
- * const schema = z.discriminatedUnion('mode', [
687
- * z.object({ mode: z.literal('create'), name: z.string(), age: z.number() }),
688
- * z.object({ mode: z.literal('edit'), id: z.number(), bio: z.string() }),
689
- * ]);
690
- *
691
- * const createSchema = extractDiscriminatedSchema({
692
- * schema,
693
- * discriminatorKey: 'mode',
694
- * discriminatorValue: 'create',
695
- * });
696
- *
697
- * // Type is EXACTLY: z.object({ mode: z.literal('create'), name: z.string(), age: z.number() })
698
- * // NOT: z.object({ mode: ..., ... }) | z.object({ mode: ..., ... }) | undefined
699
- *
700
- * if (createSchema) {
701
- * createSchema.shape.age; // ✅ TypeScript knows 'age' exists
702
- * createSchema.shape.name; // ✅ TypeScript knows 'name' exists
703
- * // createSchema.shape.id; // ❌ TypeScript error: 'id' doesn't exist on 'create' schema
704
- * }
705
- * ```
706
- *
707
- * @see {@link getSchemaDefaults} for usage with discriminated unions
708
- * @see {@link ExtractZodUnionMember} for the type-level extraction logic
709
- * @since 0.6.0
710
- */
711
- declare const extractDiscriminatedSchema: <TSchema extends z$1.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>, ReturnType extends TSchema extends z$1.ZodDiscriminatedUnion ? ExtractZodUnionMember<TSchema, TDiscriminatorKey, TDiscriminatorValue> : never>({ schema, key, value, }: {
712
- schema: TSchema;
713
- } & Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>) => ReturnType;
714
800
 
715
- export { type Discriminator, type DiscriminatorKey, type DiscriminatorValue, type Simplify, type ZodUnionCheck, canUnwrap, extractDefaultValue, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };
801
+ export { type CommonFields, type Discriminator, type DiscriminatorKey, type DiscriminatorValue, type ExtractZodByPath, type Paths, type Simplify, type ValidPaths, type ZodUnionCheck, canUnwrap, extendWithMeta, extractDefaultValue, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };