diff --git a/zod/src/__tests__/zod.ts b/zod/src/__tests__/zod.ts index c272c80..f518535 100644 --- a/zod/src/__tests__/zod.ts +++ b/zod/src/__tests__/zod.ts @@ -1,3 +1,5 @@ +import { FieldValues, Resolver, SubmitHandler, useForm } from 'react-hook-form'; +import { z } from 'zod'; import { zodResolver } from '..'; import { fields, invalidData, schema, validData } from './__fixtures__/data'; @@ -89,4 +91,86 @@ describe('zodResolver', () => { await expect(promise).rejects.toThrow('custom error'); }); + + /** + * Type inference tests + */ + it('should correctly infer the output type from a zod schema', () => { + const resolver = zodResolver(z.object({ id: z.number() })); + + expectTypeOf(resolver).toEqualTypeOf< + Resolver + >(); + }); + + it('should correctly infer the output type from a zod schema when a different input type is specified', () => { + const schema = z.object({ id: z.string() }); + const resolver = zodResolver<{ id: string }, any, z.output>( + schema, + ); + + expectTypeOf(resolver).toEqualTypeOf< + Resolver<{ id: string }, any, { id: string }> + >(); + }); + + it('should correctly infer the output type from a zod schema when a different input type is specified (only input type is specified)', () => { + const resolver = zodResolver<{ id: string }>(z.object({ id: z.string() })); + + expectTypeOf(resolver).toEqualTypeOf< + Resolver<{ id: string }, any, { id: string }> + >(); + }); + + it('should correctly infer the output type from a zod schema when different input and output types are specified', () => { + const resolver = zodResolver< + { id: string }, + { context: any }, + { id: boolean } + >(z.object({ id: z.number() })); + + expectTypeOf(resolver).toEqualTypeOf< + Resolver<{ id: string }, { context: any }, { id: boolean }> + >(); + }); + + it('should correctly infer the output type from a Zod schema for the handleSubmit function in useForm', () => { + const { handleSubmit } = useForm({ + resolver: zodResolver(z.object({ id: z.number() })), + }); + + expectTypeOf(handleSubmit).parameter(0).toEqualTypeOf< + SubmitHandler<{ + id: number; + }> + >(); + }); + + it('should correctly infer the output type from a Zod schema when a different input type is specified for the handleSubmit function in useForm', () => { + const { handleSubmit } = useForm({ + resolver: zodResolver<{ id: number }>(z.object({ id: z.string() })), + }); + + expectTypeOf(handleSubmit).parameter(0).toEqualTypeOf< + SubmitHandler<{ + id: string; + }> + >(); + }); + + it('should correctly infer the output type from a Zod schema when different input and output types are specified for the handleSubmit function in useForm', () => { + const resolver = zodResolver<{ id: string }, any, { id: boolean }>( + z.object({ id: z.number() }), + ); + + const { handleSubmit } = useForm({ + resolver, + }); + + expectTypeOf(handleSubmit).parameter(0).toEqualTypeOf< + SubmitHandler<{ + id: boolean; + }> + >(); + }); }); diff --git a/zod/src/zod.ts b/zod/src/zod.ts index 8ac4541..9ac183d 100644 --- a/zod/src/zod.ts +++ b/zod/src/zod.ts @@ -63,12 +63,12 @@ function parseErrorSchema( /** * Creates a resolver function for react-hook-form that validates form data using a Zod schema - * @param {z.ZodSchema} schema - The Zod schema used to validate the form data + * @param {z.ZodSchema} schema - The Zod schema used to validate the form data * @param {Partial} [schemaOptions] - Optional configuration options for Zod parsing * @param {Object} [resolverOptions] - Optional resolver-specific configuration * @param {('async'|'sync')} [resolverOptions.mode='async'] - Validation mode. Use 'sync' for synchronous validation * @param {boolean} [resolverOptions.raw=false] - If true, returns the raw form values instead of the parsed data - * @returns {Resolver>} A resolver function compatible with react-hook-form + * @returns {Resolver>} A resolver function compatible with react-hook-form * @throws {Error} Throws if validation fails with a non-Zod error * @example * const schema = z.object({ @@ -80,14 +80,23 @@ function parseErrorSchema( * resolver: zodResolver(schema) * }); */ -export function zodResolver( - schema: z.ZodSchema, +export function zodResolver< + Input extends FieldValues, + Context = any, + Output = undefined, + Schema extends z.ZodSchema = z.ZodSchema, +>( + schema: Schema, schemaOptions?: Partial, resolverOptions: { mode?: 'async' | 'sync'; raw?: boolean; } = {}, -): Resolver> { +): Resolver< + Input, + Context, + Output extends undefined ? z.output : Output +> { return async (values, _, options) => { try { const data = await schema[