zod icon indicating copy to clipboard operation
zod copied to clipboard

Pass schema to function and it returns infer-ed type of schema

Open denvaki opened this issue 3 years ago • 1 comments

Hey, thank you so much for this library, it is awesome!!

I try to achieve validation function for http responses, that could:

  1. accept response schema as argument
  2. validate response against schema
  3. return validated response or throw an error

Below is my maximum I could create :sweat_smile: and it works in simple cases.

async function requestWithValidate<T>(
	httpCall: () => Promise<unknown>,
	schema: z.ZodSchema<T>
): Promise<T> {
	try {
		const response = await httpCall();
		return schema.parse(response);
	} catch (e) {
		if (e instanceof ZodError) {
			e = e.format();
		}
		throw e;
	}
};

However, when I pass schema that contains ZodEffect(schema field with pre-processor for example) I have type incompatibility. I have it for Date fields and for them I get Type 'unknown' is not assignable to type 'Date'

Pre-processor:

const stringToDateConvert = z.preprocess((arg) => {
    if (typeof arg === 'string' || arg instanceof Date) {
        return new Date(arg);
    }
}, z.date());

Example schema:

z.object({
    id: z.number(),
    date: stringToDateConvert.nullable(),
}).nullable();

Could you please help with setup of generics for this function that could work even with any Zod type?

denvaki avatar Nov 18 '22 17:11 denvaki

I found workaround to infer type from _type like this:

export = async function requestWithValidate
        <T extends { parse(data: unknown, params?: Partial<ParseParams>): unknown; _type: unknown }>
         (httpCall: () => Promise<unknown>, schema: T): Promise<T['_type']> {
	try {
		const response = await httpCall();
		return schema.parse(response);
	} catch (e) {
		if (e instanceof ZodError) {
			e = e.format();
		}
		throw e;
	}
};

@colinhacks , mb we could add this into doc? I think my case faced quite often for others as well

denvaki avatar Nov 26 '22 07:11 denvaki

Is this what you are looking for?

async function requestWithValidate<Schema extends z.ZodTypeAny> (
    httpCall: () => Promise<unknown>,
    schema: Schema
): Promise<z.infer<Schema>> {
    try {
        const response = await httpCall()
        return schema.parse( response )
    } catch ( e ) {
        if ( e instanceof z.ZodError ) {
            e = e.format()
        }
        throw e
    }
}

JacobWeisenburger avatar Jan 03 '23 18:01 JacobWeisenburger