Search code examples
javascripttypescriptzod

Trouble with type inference in factory pattern implementation with Zod schema


How can I infer types based on specified function arguments when the schema is defined within the parent function? I'm attempting to implement a factory pattern but encountering issues with type inference. Here's an example of what I'm trying to achieve:

import { z } from 'zod';

export const makeClient = <T extends string>({
  schemas,
}: {
  schemas: {
    [key in T]: z.ZodSchema;
  };
}) => {
  type Keys = keyof typeof schemas;
  type Values<K extends Keys> = z.infer<typeof schemas[K]>;

  return {
    async findOne<K extends Keys>({
      name,
      filter,
    }: {
      name: K;
      filter: Values<K>;
    }) {
      return { name, filter };
    },
  };
};

const client = makeClient({
  schemas: {
    test: z.object({
      value: z.number(),
    }),
  },
});

client.findOne({
  name: "test", // this is inferred
  filter: {
    // not inferred
  }
})

I believe it should be possible to achieve type inference in this scenario, but I'm having trouble figuring out how. Any guidance would be appreciated.


Solution

  • I've waited for commenters to respond, but it's been some time now. With the directions from Bergi in comments, I've managed to fix it like below:

    import { z } from 'zod';
    
    export const makeClient = <S extends Record<string, z.ZodSchema>>({
      schemas,
    }: {
      schemas: S;
    }) => {
      return {
        async findOne<K extends keyof S>({
          name,
          filter,
        }: {
          name: K;
          filter: z.infer<S[K]>;
        }) {
          return { name, filter };
        },
      };
    };
    
    const client = makeClient({
      schemas: {
        test: z.object({
          value: z.number(),
        }),
      },
    });
    
    client.findOne({
      name: "test",
      filter: {
        // now inferred
      }
    })