There doesn't seem to be a direct way to do this sort of thing from the library, but you can dig into their _
private fields and get the functionality you're looking for.
There are some risks associated with this approach because library maintainers typically don't guarantee stability in these private properties. If you're relying on this behavior you may need to be extra careful about version bumps.
Ok disclaimer out of the way, something like this is possible. Extending this to more types is left as an exercise for the reader:
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: {
// The switch wasn't able to infer this but the cast should
// be safe.
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));
Here, I've specified no defaults but the code still outputs what you expected. If you specified "foo" as the default for name
the code will output { name: "foo", age: null }
A shorter approach would be to simply dig in one layer into the _def
of your schema looking for defaultValue
functions to call, but I think the given approach is more principled since it could be extended to support every core zod
schema type.
One last word of warning, some of the zod
types are not as straightforward to handle as others. Something like z.number
could be reasonably given a default of 0
, but z.union
or z.intersection
would have interesting recursive cases.
It might be worth building out a library just for this handling or else opening an issue with the repo to make it part of the offered api.