Skip to content

Instantly share code, notes, and snippets.

@TonyGravagno
Last active June 3, 2024 00:30
Show Gist options
  • Save TonyGravagno/2b744ceb99e415c4b53e8b35b309c29c to your computer and use it in GitHub Desktop.
Save TonyGravagno/2b744ceb99e415c4b53e8b35b309c29c to your computer and use it in GitHub Desktop.
Create a default object from a Zod schema
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()
}
@yamatohagi
Copy link

Has this been resolved?

@man-trackunit
Copy link

Would it make sense/possible to abstract it to also work for .catch()?

@FondueBug
Copy link

Thanks for your contribution, I really appreciate it.

@TonyGravagno
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment