diff --git a/zod/src/__tests__/zod-v4-mini.ts b/zod/src/__tests__/zod-v4-mini.ts index 54c5681..1787717 100644 --- a/zod/src/__tests__/zod-v4-mini.ts +++ b/zod/src/__tests__/zod-v4-mini.ts @@ -111,6 +111,23 @@ describe('zodResolver', () => { >(); }); + it('should infer resolver types from a Zod v4 mini structural schema type', () => { + type StructuralSchema = { + _zod: { + input: { id: number }; + output: { id: string }; + }; + }; + + type InferredResolver = ReturnType< + typeof zodResolver + >; + + expectTypeOf().toEqualTypeOf< + Resolver<{ id: number }, unknown, { id: string }> + >(); + }); + it('should correctly infer the output type from a zod schema using a transform', () => { const resolver = zodResolver( z.object({ diff --git a/zod/src/__tests__/zod-v4.ts b/zod/src/__tests__/zod-v4.ts index 0a043bc..ec04d1b 100644 --- a/zod/src/__tests__/zod-v4.ts +++ b/zod/src/__tests__/zod-v4.ts @@ -118,6 +118,23 @@ describe('zodResolver', () => { >(); }); + it('should infer resolver types from a Zod v4 structural schema type', () => { + type StructuralSchema = { + _zod: { + input: { id: number }; + output: { id: string }; + }; + }; + + type InferredResolver = ReturnType< + typeof zodResolver + >; + + expectTypeOf().toEqualTypeOf< + Resolver<{ id: number }, unknown, { id: string }> + >(); + }); + it('should correctly infer the output type from a zod schema using a transform', () => { const resolver = zodResolver( z.object({ id: z.number().transform((val) => String(val)) }), diff --git a/zod/src/zod.ts b/zod/src/zod.ts index 9093c81..909ebb5 100644 --- a/zod/src/zod.ts +++ b/zod/src/zod.ts @@ -148,6 +148,30 @@ interface Zod3Type { typeName: string; }; } +type Zod4TypeLike = { + _zod: { + input?: unknown; + output?: unknown; + }; +}; +type Zod4SchemaLike = { + _zod: { + input: Input; + output: Output; + }; +}; +type Zod4Input = T extends { + _zod: { input?: infer I }; +} + ? I extends FieldValues + ? I + : FieldValues + : FieldValues; +type Zod4Output = T extends { + _zod: { output?: infer O }; +} + ? O + : unknown; // some type magic to make versions pre-3.25.0 still work type IsUnresolved = PropertyKey extends keyof T ? true : false; @@ -197,26 +221,21 @@ export function zodResolver( resolverOptions: RawResolverOptions, ): Resolver; // the Zod 4 overloads need to be generic for complicated reasons -export function zodResolver< - Input extends FieldValues, - Context, - Output, - T extends z4.$ZodType = z4.$ZodType, ->( +export function zodResolver( schema: T, schemaOptions?: Zod4ParseParams, // already partial resolverOptions?: NonRawResolverOptions, -): Resolver, Context, z4.output>; -export function zodResolver< - Input extends FieldValues, - Context, - Output, - T extends z4.$ZodType = z4.$ZodType, ->( - schema: z4.$ZodType, +): Resolver, Context, Zod4Output>; +export function zodResolver( + schema: Zod4SchemaLike, + schemaOptions?: Zod4ParseParams, // already partial + resolverOptions?: NonRawResolverOptions, +): Resolver; +export function zodResolver( + schema: Zod4SchemaLike, schemaOptions: Zod4ParseParams | undefined, // already partial resolverOptions: RawResolverOptions, -): Resolver, Context, z4.input>; +): Resolver; /** * Creates a resolver function for react-hook-form that validates form data using a Zod schema * @param {z3.ZodSchema} schema - The Zod schema used to validate the form data