I have a Zod shape with quite lots of keys. I need another shape with the same keys, but different types. With plain Typescript I could define them such as
type TypeA = {
something1: number
something2: number
...
somethingN: number
}
type TypeB = {
[Property in keyof TypeA]: string
}
and then whenever I need to add or remove keys I only need to modify the TypeA. I would like to recreate this with Zod, so that I would get shape/parser for both types. Is that possible and if yes, how can I do that?
This wont work as it only gives me the shape for the first type
const TypeA = z.object({
something1: z.number(),
something2: z.number(),
...
somethingN: z.number()
)}
type TypeA = z.infer<typeof TypeA>
type TypeB = {
[Property in keyof TypeA]: string
}
// No shape for TypeB
Is there a way in zod to iterate over the keys and assign them some type (or is it still called shape before the type is inferred?)
CodePudding user response:
// declare property list once here, as string const string array.
const PropertyKeysNames = ["something1", "something2", "somethingN"] as const satisfies readonly string[];
type PropertyKeys = typeof PropertyKeysNames[number];
// create both your TypeA and TypeB, using indexed, as you done.
type TypeA = {[Property in PropertyKeys]: number;};
type TypeB = {[Property in PropertyKeys]: string;};
// Now you need an instance of zod schema, not a type.
// we dynamically create it at runtime
/**
* create a zod object with PropertyKeysNames as keys, each has type of a zod type in parameter
* @param zobject the zod object of each properties, typically z.string()
* @returns same a z.object(...) call
*/
const build = (zobject: z.ZodTypeAny): ReturnType<typeof z.object> => {
const shape: { [k: string]: z.ZodTypeAny } = {};
for (const key of PropertyKeysNames) {
shape[key] = zobject;
}
return z.object(shape);
};
// The result
const ZobjectWithString = build(z.string());
tests :
const ObjectsToTest = [
{something1: "something", something2: "something else", somethingN: "bob l'eponge"}, // OK
{something1: "something", something2: "something else", somethingN: "bob l'eponge", somethingMORE:"more"}, // OK
{somethingN: "bob l'eponge"}, // failed
{ username: "Ludwig" }, // failed
undefined
]
let i = 0;
for(const o of ObjectsToTest){
try {
ZobjectWithString.parse(o);
console.log("test " i " ok");
}catch(e){
console.log("test " i " failed");
}
i;
}
Hopes that could help