Home > Net >  How to implement "Property in keyof Type" shape with Zod?
How to implement "Property in keyof Type" shape with Zod?

Time:01-17

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

  • Related