0
votes

So I'm trying to make a helper that will allow me to easily create empty/initial form values from a zod schema that will validate the whole/complete form. In other words I want the schema to require every field to be populated, but initial values might be nullable...

Anyway I am confused by an issue demonstrated in this sandbox

With the following code:

const schema = z.object({
    deep: z.object({
        union: z.enum(['a', 'b'])
    })
})

function makeObj<D extends Partial<z.infer<typeof schema>>, S extends z.ZodTypeAny>(schema: S, v: D): z.ZodType<DeepReplace<z.infer<S>, D>> {
    return schema as z.ZodType<DeepReplace<z.infer<S>, D>>
}

const obj = makeObj(schema, {
    deep: {
        union: 'a'
    }
}).parse({})

obj is correctly typed:

const obj: {
    deep: {
        union: "a";
    };
}

image

But if I replace the function declaration with this line:

function makeObj<D extends Partial<z.infer<S>>, S extends z.ZodTypeAny>(schema: S, v: D): z.ZodType<DeepReplace<z.infer<S>, D>> {
    return schema as z.ZodType<DeepReplace<z.infer<S>, D>>
}
const obj = makeObj(schema, {
    deep: {
        union: 'a'
    }
}).parse({})

Now type inference is broken:

const obj: {
    deep: {
        union: null;
    } | {
        union: "a" | "b";
    };
}

image

Unless, I have found I put "as const" on the second argument:

const obj = makeObj(schema, {
    deep: {
        union: 'a'
    }
} as const).parse({})
  • It appears that this is only a problem when union types are involved
  • I'd love not to have to bother with the as const everywhere.
  • And mostly I'd like to understand why using z.infer<> is the source of the issue!

Thanks!

-Morgan