Skip to content

Commit cf569f3

Browse files
committed
feat: implement typed Input/Output interface for resolvers
1 parent ded1746 commit cf569f3

File tree

2 files changed

+98
-5
lines changed

2 files changed

+98
-5
lines changed

zod/src/__tests__/zod.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { FieldValues, Resolver, SubmitHandler, useForm } from 'react-hook-form';
2+
import { z } from 'zod';
13
import { zodResolver } from '..';
24
import { fields, invalidData, schema, validData } from './__fixtures__/data';
35

@@ -89,4 +91,86 @@ describe('zodResolver', () => {
8991

9092
await expect(promise).rejects.toThrow('custom error');
9193
});
94+
95+
/**
96+
* Type inference tests
97+
*/
98+
it('should correctly infer the output type from a zod schema', () => {
99+
const resolver = zodResolver(z.object({ id: z.number() }));
100+
101+
expectTypeOf(resolver).toEqualTypeOf<
102+
Resolver<FieldValues, any, { id: number }>
103+
>();
104+
});
105+
106+
it('should correctly infer the output type from a zod schema when a different input type is specified', () => {
107+
const schema = z.object({ id: z.string() });
108+
const resolver = zodResolver<{ id: string }, any, z.output<typeof schema>>(
109+
schema,
110+
);
111+
112+
expectTypeOf(resolver).toEqualTypeOf<
113+
Resolver<{ id: string }, any, { id: string }>
114+
>();
115+
});
116+
117+
it('should correctly infer the output type from a zod schema when a different input type is specified (only input type is specified)', () => {
118+
const resolver = zodResolver<{ id: string }>(z.object({ id: z.string() }));
119+
120+
expectTypeOf(resolver).toEqualTypeOf<
121+
Resolver<{ id: string }, any, { id: string }>
122+
>();
123+
});
124+
125+
it('should correctly infer the output type from a zod schema when different input and output types are specified', () => {
126+
const resolver = zodResolver<
127+
{ id: string },
128+
{ context: any },
129+
{ id: boolean }
130+
>(z.object({ id: z.number() }));
131+
132+
expectTypeOf(resolver).toEqualTypeOf<
133+
Resolver<{ id: string }, { context: any }, { id: boolean }>
134+
>();
135+
});
136+
137+
it('should correctly infer the output type from a Zod schema for the handleSubmit function in useForm', () => {
138+
const { handleSubmit } = useForm({
139+
resolver: zodResolver(z.object({ id: z.number() })),
140+
});
141+
142+
expectTypeOf(handleSubmit).parameter(0).toEqualTypeOf<
143+
SubmitHandler<{
144+
id: number;
145+
}>
146+
>();
147+
});
148+
149+
it('should correctly infer the output type from a Zod schema when a different input type is specified for the handleSubmit function in useForm', () => {
150+
const { handleSubmit } = useForm({
151+
resolver: zodResolver<{ id: number }>(z.object({ id: z.string() })),
152+
});
153+
154+
expectTypeOf(handleSubmit).parameter(0).toEqualTypeOf<
155+
SubmitHandler<{
156+
id: string;
157+
}>
158+
>();
159+
});
160+
161+
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', () => {
162+
const resolver = zodResolver<{ id: string }, any, { id: boolean }>(
163+
z.object({ id: z.number() }),
164+
);
165+
166+
const { handleSubmit } = useForm({
167+
resolver,
168+
});
169+
170+
expectTypeOf(handleSubmit).parameter(0).toEqualTypeOf<
171+
SubmitHandler<{
172+
id: boolean;
173+
}>
174+
>();
175+
});
92176
});

zod/src/zod.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ function parseErrorSchema(
6363

6464
/**
6565
* Creates a resolver function for react-hook-form that validates form data using a Zod schema
66-
* @param {z.ZodSchema<TFieldValues>} schema - The Zod schema used to validate the form data
66+
* @param {z.ZodSchema<Input>} schema - The Zod schema used to validate the form data
6767
* @param {Partial<z.ParseParams>} [schemaOptions] - Optional configuration options for Zod parsing
6868
* @param {Object} [resolverOptions] - Optional resolver-specific configuration
6969
* @param {('async'|'sync')} [resolverOptions.mode='async'] - Validation mode. Use 'sync' for synchronous validation
7070
* @param {boolean} [resolverOptions.raw=false] - If true, returns the raw form values instead of the parsed data
71-
* @returns {Resolver<z.infer<typeof schema>>} A resolver function compatible with react-hook-form
71+
* @returns {Resolver<z.ouput<typeof schema>>} A resolver function compatible with react-hook-form
7272
* @throws {Error} Throws if validation fails with a non-Zod error
7373
* @example
7474
* const schema = z.object({
@@ -80,14 +80,23 @@ function parseErrorSchema(
8080
* resolver: zodResolver(schema)
8181
* });
8282
*/
83-
export function zodResolver<TFieldValues extends FieldValues>(
84-
schema: z.ZodSchema<TFieldValues, any, any>,
83+
export function zodResolver<
84+
Input extends FieldValues,
85+
Context = any,
86+
Output = undefined,
87+
Schema extends z.ZodSchema<any, any, any> = z.ZodSchema<any, any, any>,
88+
>(
89+
schema: Schema,
8590
schemaOptions?: Partial<z.ParseParams>,
8691
resolverOptions: {
8792
mode?: 'async' | 'sync';
8893
raw?: boolean;
8994
} = {},
90-
): Resolver<z.infer<typeof schema>> {
95+
): Resolver<
96+
Input,
97+
Context,
98+
Output extends undefined ? z.output<Schema> : Output
99+
> {
91100
return async (values, _, options) => {
92101
try {
93102
const data = await schema[

0 commit comments

Comments
 (0)