Skip to content

Instantly share code, notes, and snippets.

@TonyGravagno
Last active May 5, 2025 05:23
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()
}
@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.

@mpint
Copy link

mpint commented Apr 21, 2025

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

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