Search code examples
typescriptgenericszod

Generic type function converting property with a default value to optional


I have written a helper function that allows me to validate FormData objects using Zod. Since this can be used for various schemas, I have used a generic type.

Here is the validate function:

export function validate<T>(
    formData: FormData,
    schema: z.ZodSchema<T>
): { validatedData: T; errors: null } | { validatedData: null; errors: ActionErrors<T> } {
    const body = parseFormData(formData);
    const validated = schema.safeParse(body);

    if (!validated.success) {
        return {
            validatedData: null,
            errors: formatZodErrors<T>(validated.error),
        };
    }

    return { validatedData: validated.data, errors: null };
}

The problem I'm having is, when using the helper function it seems to be turning the properties with default values set in the schemas to optional.

Here is the schema I am passing into the validate function.

export const createUserSchema = z.object({
    name: fullName(),
    email: email(),
    sendActivationEmail: z.boolean().default(true)
});

Intellisense showing the property as optional

The fact this is being returned with an optional property just doesn't make any sense to me. Any explanation or help would be much appreciated 😊.

Worth noting that if I don't run this through the helper function and parse it directly in the action, it isn't returning as optional which you would expect.


Solution

  • Try this:

    export function validate<T extends z.ZodTypeAny>(
        formData: FormData,
        schema: T
    ) {
        const body = parseFormData(formData);
        const validated = schema.safeParse(body);
    
        if (!validated.success) {
            return {
                validatedData: null,
                errors: formatZodErrors<z.infer<T>>(validated.error),
            };
        }
    
        return { validatedData: validated.data as z.infer<T>, errors: null };
    }
    

    I can't explain better than the document itself. You can read more at writing-generic-functions, inferring-the-inferred-type