There isn't a straightforward method within the library to achieve this specific task, but by delving into their private fields denoted with _
, you can access the desired functionality.
However, it is important to note that this approach comes with certain risks as library developers typically do not assure stability of these private properties. If your code depends on this behavior, caution should be exercised particularly when updating versions.
Setting aside the disclaimer, the potential for something like this exists. The expansion of this concept to encompass more types is left as a challenge for the individual:
import { z } from "zod";
const schema = z.object({
name: z.string(),
age: z.number().nullable()
});
const schemaDefaults = <Schema extends z.ZodFirstPartySchemaTypes>(
schema: Schema
): z.TypeOf<Schema> => {
switch (schema._def.typeName) {
case z.ZodFirstPartyTypeKind.ZodDefault:
return schema._def.defaultValue();
case z.ZodFirstPartyTypeKind.ZodObject: {
// Although the switch statement may not have deduced this, the cast seems secure.
return Object.fromEntries(
Object.entries(
(schema as z.SomeZodObject).shape
).map(([key, value]) => [key, schemaDefaults(value)])
);
}
case z.ZodFirstPartyTypeKind.ZodString:
return "";
case z.ZodFirstPartyTypeKind.ZodNull:
return null;
case z.ZodFirstPartyTypeKind.ZodNullable:
return null;
// etc
default:
throw new Error(`Unsupported type ${schema._type}`);
}
};
console.log(schemaDefaults(schema));
In this scenario, no defaults have been specified, yet the code still yields the expected output. For instance, if "foo" was designated as the default for name
, the result would be
{ name: "foo", age: null }
An alternate, more concise approach could involve exploring one layer deep into the _def
of your schema in search of functions like defaultValue
to invoke. However, the provided method is deemed superior due to its potential to accommodate all fundamental zod
schema types.
To conclude, it is worth mentioning that some zod
types pose greater challenges than others. While assigning a default value of 0
to z.number
may seem reasonable, handling cases involving z.union
or z.intersection
presents complex recursive scenarios.
Perhaps creating a dedicated library specifically for this purpose or submitting a feature request to incorporate it into the official API could prove beneficial.