@zod-utils/core 1.2.0 → 2.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 +54 -5
- package/dist/index.d.mts +250 -164
- package/dist/index.d.ts +250 -164
- package/dist/index.js +68 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +55 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
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
|
-
|
|
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
|
-
|
|
444
|
+
name: TPath;
|
|
219
445
|
discriminator?: Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>;
|
|
220
|
-
}):
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var z3 = require('zod');
|
|
4
4
|
|
|
5
5
|
function _interopNamespace(e) {
|
|
6
6
|
if (e && e.__esModule) return e;
|
|
@@ -20,7 +20,7 @@ function _interopNamespace(e) {
|
|
|
20
20
|
return Object.freeze(n);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
var
|
|
23
|
+
var z3__namespace = /*#__PURE__*/_interopNamespace(z3);
|
|
24
24
|
|
|
25
25
|
var __defProp = Object.defineProperty;
|
|
26
26
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -38,14 +38,32 @@ var __spreadValues = (a, b) => {
|
|
|
38
38
|
}
|
|
39
39
|
return a;
|
|
40
40
|
};
|
|
41
|
+
var extractDiscriminatedSchema = ({
|
|
42
|
+
schema,
|
|
43
|
+
key,
|
|
44
|
+
value
|
|
45
|
+
}) => {
|
|
46
|
+
if (!(schema instanceof z3.z.ZodDiscriminatedUnion)) {
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
return schema.options.find((option) => {
|
|
50
|
+
if (option instanceof z3.z.ZodObject) {
|
|
51
|
+
const targetField = option.shape[String(key)];
|
|
52
|
+
if (!targetField) return false;
|
|
53
|
+
const parseResult = targetField.safeParse(value);
|
|
54
|
+
return parseResult.success;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
});
|
|
58
|
+
};
|
|
41
59
|
function canUnwrap(field) {
|
|
42
60
|
return "unwrap" in field && typeof field.unwrap === "function";
|
|
43
61
|
}
|
|
44
62
|
function tryStripNullishOnly(field) {
|
|
45
|
-
if (field instanceof
|
|
63
|
+
if (field instanceof z3.z.ZodUnion) {
|
|
46
64
|
const unionOptions = [...field.def.options];
|
|
47
65
|
const filteredOptions = unionOptions.filter(
|
|
48
|
-
(option) => !(option instanceof
|
|
66
|
+
(option) => !(option instanceof z3.z.ZodNull) && !(option instanceof z3.z.ZodUndefined)
|
|
49
67
|
);
|
|
50
68
|
const firstOption = filteredOptions[0];
|
|
51
69
|
if (firstOption && filteredOptions.length === 1) {
|
|
@@ -55,34 +73,34 @@ function tryStripNullishOnly(field) {
|
|
|
55
73
|
return false;
|
|
56
74
|
}
|
|
57
75
|
var getPrimitiveType = (field) => {
|
|
58
|
-
if (field instanceof
|
|
76
|
+
if (field instanceof z3.z.ZodArray) {
|
|
59
77
|
return field;
|
|
60
78
|
}
|
|
61
79
|
if (canUnwrap(field)) {
|
|
62
80
|
return getPrimitiveType(field.unwrap());
|
|
63
81
|
}
|
|
64
|
-
if (field instanceof
|
|
82
|
+
if (field instanceof z3.z.ZodUnion) {
|
|
65
83
|
const unwrapped = tryStripNullishOnly(field);
|
|
66
84
|
if (unwrapped !== false) {
|
|
67
85
|
return getPrimitiveType(unwrapped);
|
|
68
86
|
}
|
|
69
87
|
return field;
|
|
70
88
|
}
|
|
71
|
-
if (field instanceof
|
|
89
|
+
if (field instanceof z3.z.ZodPipe && field.def.in instanceof z3.z.ZodType) {
|
|
72
90
|
return getPrimitiveType(field.def.in);
|
|
73
91
|
}
|
|
74
92
|
return field;
|
|
75
93
|
};
|
|
76
94
|
function removeDefault(field) {
|
|
77
|
-
if (field instanceof
|
|
95
|
+
if (field instanceof z3.z.ZodDefault) {
|
|
78
96
|
return field.unwrap();
|
|
79
97
|
}
|
|
80
|
-
if ("innerType" in field.def && field.def.innerType instanceof
|
|
98
|
+
if ("innerType" in field.def && field.def.innerType instanceof z3.z.ZodType) {
|
|
81
99
|
const inner = removeDefault(field.def.innerType);
|
|
82
|
-
if (field instanceof
|
|
100
|
+
if (field instanceof z3.z.ZodOptional) {
|
|
83
101
|
return inner.optional();
|
|
84
102
|
}
|
|
85
|
-
if (field instanceof
|
|
103
|
+
if (field instanceof z3.z.ZodNullable) {
|
|
86
104
|
return inner.nullable();
|
|
87
105
|
}
|
|
88
106
|
}
|
|
@@ -90,7 +108,7 @@ function removeDefault(field) {
|
|
|
90
108
|
}
|
|
91
109
|
var requiresValidInput = (field) => {
|
|
92
110
|
const defaultRemovedField = removeDefault(field);
|
|
93
|
-
if (!(defaultRemovedField instanceof
|
|
111
|
+
if (!(defaultRemovedField instanceof z3.z.ZodType)) {
|
|
94
112
|
return false;
|
|
95
113
|
}
|
|
96
114
|
const undefinedResult = defaultRemovedField.safeParse(void 0).success;
|
|
@@ -105,41 +123,23 @@ function getFieldChecks(field) {
|
|
|
105
123
|
const primitiveType = getPrimitiveType(field);
|
|
106
124
|
return ((_a = primitiveType.def.checks) == null ? void 0 : _a.map((check) => check._zod.def)) || [];
|
|
107
125
|
}
|
|
108
|
-
var extractDiscriminatedSchema = ({
|
|
109
|
-
schema,
|
|
110
|
-
key,
|
|
111
|
-
value
|
|
112
|
-
}) => {
|
|
113
|
-
if (!(schema instanceof z2.z.ZodDiscriminatedUnion)) {
|
|
114
|
-
return void 0;
|
|
115
|
-
}
|
|
116
|
-
return schema.options.find((option) => {
|
|
117
|
-
if (option instanceof z2.z.ZodObject) {
|
|
118
|
-
const targetField = option.shape[String(key)];
|
|
119
|
-
if (!targetField) return false;
|
|
120
|
-
const parseResult = targetField.safeParse(value);
|
|
121
|
-
return parseResult.success;
|
|
122
|
-
}
|
|
123
|
-
return false;
|
|
124
|
-
});
|
|
125
|
-
};
|
|
126
126
|
|
|
127
127
|
// src/defaults.ts
|
|
128
128
|
function extractDefaultValue(field) {
|
|
129
|
-
if (field instanceof
|
|
129
|
+
if (field instanceof z3__namespace.ZodDefault) {
|
|
130
130
|
return field.def.defaultValue;
|
|
131
131
|
}
|
|
132
132
|
if (canUnwrap(field)) {
|
|
133
133
|
return extractDefaultValue(field.unwrap());
|
|
134
134
|
}
|
|
135
|
-
if (field instanceof
|
|
135
|
+
if (field instanceof z3__namespace.ZodUnion) {
|
|
136
136
|
const unwrapped = tryStripNullishOnly(field);
|
|
137
137
|
if (unwrapped !== false) {
|
|
138
138
|
return extractDefaultValue(unwrapped);
|
|
139
139
|
}
|
|
140
140
|
return void 0;
|
|
141
141
|
}
|
|
142
|
-
if (field instanceof
|
|
142
|
+
if (field instanceof z3__namespace.ZodPipe && field.def.in instanceof z3__namespace.ZodType) {
|
|
143
143
|
return extractDefaultValue(field.def.in);
|
|
144
144
|
}
|
|
145
145
|
return void 0;
|
|
@@ -147,13 +147,13 @@ function extractDefaultValue(field) {
|
|
|
147
147
|
function getSchemaDefaults(schema, options) {
|
|
148
148
|
const primitiveSchema = getPrimitiveType(schema);
|
|
149
149
|
let targetSchema;
|
|
150
|
-
if (primitiveSchema instanceof
|
|
150
|
+
if (primitiveSchema instanceof z3__namespace.ZodDiscriminatedUnion) {
|
|
151
151
|
if (options == null ? void 0 : options.discriminator) {
|
|
152
152
|
targetSchema = extractDiscriminatedSchema(__spreadValues({
|
|
153
153
|
schema: primitiveSchema
|
|
154
154
|
}, options.discriminator));
|
|
155
155
|
}
|
|
156
|
-
} else if (primitiveSchema instanceof
|
|
156
|
+
} else if (primitiveSchema instanceof z3__namespace.ZodObject) {
|
|
157
157
|
targetSchema = primitiveSchema;
|
|
158
158
|
}
|
|
159
159
|
const defaults = {};
|
|
@@ -171,26 +171,50 @@ function getSchemaDefaults(schema, options) {
|
|
|
171
171
|
}
|
|
172
172
|
function extractFieldFromSchema({
|
|
173
173
|
schema,
|
|
174
|
-
|
|
174
|
+
name,
|
|
175
175
|
discriminator
|
|
176
176
|
}) {
|
|
177
|
-
let
|
|
177
|
+
let currentSchema;
|
|
178
178
|
const primitiveSchema = getPrimitiveType(schema);
|
|
179
|
-
if (primitiveSchema instanceof
|
|
179
|
+
if (primitiveSchema instanceof z3.z.ZodDiscriminatedUnion) {
|
|
180
180
|
if (discriminator) {
|
|
181
|
-
|
|
181
|
+
currentSchema = extractDiscriminatedSchema(__spreadValues({
|
|
182
182
|
schema: primitiveSchema
|
|
183
183
|
}, discriminator));
|
|
184
184
|
}
|
|
185
|
-
} else if (primitiveSchema instanceof
|
|
186
|
-
|
|
185
|
+
} else if (primitiveSchema instanceof z3.z.ZodObject) {
|
|
186
|
+
currentSchema = primitiveSchema;
|
|
187
|
+
}
|
|
188
|
+
if (!currentSchema) return void 0;
|
|
189
|
+
const segments = String(name).split(".");
|
|
190
|
+
for (const segment of segments) {
|
|
191
|
+
if (!currentSchema) return void 0;
|
|
192
|
+
const unwrapped = getPrimitiveType(currentSchema);
|
|
193
|
+
if (unwrapped instanceof z3.z.ZodObject) {
|
|
194
|
+
currentSchema = unwrapped.shape[segment];
|
|
195
|
+
} else if (unwrapped instanceof z3.z.ZodArray) {
|
|
196
|
+
if (/^\d+$/.test(segment) && unwrapped.element instanceof z3.z.ZodType) {
|
|
197
|
+
currentSchema = unwrapped.element;
|
|
198
|
+
} else {
|
|
199
|
+
return void 0;
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
return void 0;
|
|
203
|
+
}
|
|
187
204
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
205
|
+
return currentSchema;
|
|
206
|
+
}
|
|
207
|
+
function extendWithMeta(field, transform) {
|
|
208
|
+
const transformedField = transform(field);
|
|
209
|
+
const meta = field.meta();
|
|
210
|
+
if (!meta) {
|
|
211
|
+
return transformedField;
|
|
212
|
+
}
|
|
213
|
+
return transformedField.meta(__spreadValues({}, meta));
|
|
191
214
|
}
|
|
192
215
|
|
|
193
216
|
exports.canUnwrap = canUnwrap;
|
|
217
|
+
exports.extendWithMeta = extendWithMeta;
|
|
194
218
|
exports.extractDefaultValue = extractDefaultValue;
|
|
195
219
|
exports.extractDiscriminatedSchema = extractDiscriminatedSchema;
|
|
196
220
|
exports.extractFieldFromSchema = extractFieldFromSchema;
|