-
-
Save TonyGravagno/2b744ceb99e415c4b53e8b35b309c29c to your computer and use it in GitHub Desktop.
| import { z } from 'zod' | |
| /** | |
| * @summary Function returns default object from Zod schema | |
| * @version 23.05.15.2 | |
| * @link https://gist.github.com/TonyGravagno/2b744ceb99e415c4b53e8b35b309c29c | |
| * @author Jacob Weisenburger, Josh Andromidas, Thomas Moiluiavon, Tony Gravagno | |
| * @param schema z.object schema definition | |
| * @param options Optional object, see Example for details | |
| * @returns Object of type schema with defaults for all fields | |
| * @example | |
| * const schema = z.object( { ... } ) | |
| * const default1 = defaultInstance<typeof schema>(schema) | |
| * const default2 = defaultInstance<typeof schema>( | |
| * schema,{ // toggle from these defaults if required | |
| * defaultArrayEmpty: false, | |
| * defaultDateEmpty: false, | |
| * defaultDateUndefined: false, | |
| * defaultDateNull: false, | |
| * } ) | |
| */ | |
| export function defaultInstance<T extends z.ZodTypeAny>( | |
| schema: z.AnyZodObject | z.ZodEffects<any>, | |
| options: object = {} | |
| ): z.infer<T> { | |
| const defaultArrayEmpty = 'defaultArrayEmpty' in options ? options.defaultArrayEmpty : false | |
| const defaultDateEmpty = 'defaultDateEmpty' in options ? options.defaultDateEmpty : false | |
| const defaultDateUndefined = 'defaultDateUndefined' in options ? options.defaultDateUndefined : false | |
| const defaultDateNull = 'defaultDateNull' in options ? options.defaultDateNull : false | |
| function run(): z.infer<T> { | |
| if (schema instanceof z.ZodEffects) { | |
| if (schema.innerType() instanceof z.ZodEffects) { | |
| return defaultInstance(schema.innerType(), options) // recursive ZodEffect | |
| } | |
| // return schema inner shape as a fresh zodObject | |
| return defaultInstance(z.ZodObject.create(schema.innerType().shape), options) | |
| } | |
| if (schema instanceof z.ZodType) { | |
| let the_shape = schema.shape as z.ZodAny // eliminates 'undefined' issue | |
| let entries = Object.entries(the_shape) | |
| let temp = entries.map(([key, value]) => { | |
| let this_default = | |
| value instanceof z.ZodEffects ? defaultInstance(value, options) : getDefaultValue(value) | |
| return [key, this_default] | |
| }) | |
| return Object.fromEntries(temp) | |
| } else { | |
| console.log(`Error: Unable to process this schema`) | |
| return null // unknown or undefined here results in complications | |
| } | |
| function getDefaultValue(dschema: z.ZodTypeAny): any { | |
| console.dir(dschema) | |
| if (dschema instanceof z.ZodDefault) { | |
| if (!('_def' in dschema)) return undefined // error | |
| if (!('defaultValue' in dschema._def)) return undefined // error | |
| return dschema._def.defaultValue() | |
| } | |
| if (dschema instanceof z.ZodArray) { | |
| if (!('_def' in dschema)) return undefined // error | |
| if (!('type' in dschema._def)) return undefined // error | |
| // return empty array or array with one empty typed element | |
| return defaultArrayEmpty ? [] : [getDefaultValue(dschema._def.type as z.ZodAny)] | |
| } | |
| if (dschema instanceof z.ZodString) return '' | |
| if (dschema instanceof z.ZodNumber || dschema instanceof z.ZodBigInt) { | |
| let value = dschema.minValue ?? 0 | |
| return value | |
| } | |
| if (dschema instanceof z.ZodDate) { | |
| let value = defaultDateEmpty | |
| ? '' | |
| : defaultDateNull | |
| ? null | |
| : defaultDateUndefined | |
| ? undefined | |
| : (dschema as z.ZodDate).minDate | |
| return value | |
| } | |
| if (dschema instanceof z.ZodSymbol) return '' | |
| if (dschema instanceof z.ZodBoolean) return false | |
| if (dschema instanceof z.ZodNull) return null | |
| if (dschema instanceof z.ZodPipeline) { | |
| if (!('out' in dschema._def)) return undefined // error | |
| return getDefaultValue(dschema._def.out) | |
| } | |
| if (dschema instanceof z.ZodObject) { | |
| return defaultInstance(dschema, options) | |
| } | |
| if (dschema instanceof z.ZodAny && !('innerType' in dschema._def)) return undefined // error? | |
| return getDefaultValue(dschema._def.innerType) | |
| } | |
| } | |
| return run() | |
| } |
Would it make sense/possible to abstract it to also work for .catch()?
Thanks for your contribution, I really appreciate it.
Update of ... non updates. I'm sorry that I haven't had time to come back to enhance this code. I was hoping that by now there would be a solution implemented in Zod, and have been holding out on continuing updates here. I'll hold out a bit longer. I haven't been active in my apps that use Zod recently so I'm a bit rusty on all of this.
The code here isn't ideal. There are a number of faults in more complex use cases. Here is my suggested approach to addressing this topic:
- Watch this gist, but don't count on it being updated or for generous submissions to be incorporated.
- Watch colinhacks/zod#1953
- Be aware that any system that provides a default value for general use cases may not be ideal for a large number of specific use cases.
- Get lots of sleep. Drink lots of coffee. Be good to yourself and others.
- Have a great day.
Anyone have a chance to see if the new zod version improves this experience?
Update: Upgrading to
zod@4beta breaks this function and the new version doesn't appear to solve the default schema problem
Has this been resolved?