-
-
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@4
beta breaks this function and the new version doesn't appear to solve the default schema problem
Has this been resolved?