Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions zod/src/__tests__/zod-v4-mini.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<unknown, StructuralSchema>
>;

expectTypeOf<InferredResolver>().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({
Expand Down
17 changes: 17 additions & 0 deletions zod/src/__tests__/zod-v4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<unknown, StructuralSchema>
>;

expectTypeOf<InferredResolver>().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)) }),
Expand Down
49 changes: 34 additions & 15 deletions zod/src/zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,30 @@ interface Zod3Type<O = unknown, I = unknown> {
typeName: string;
};
}
type Zod4TypeLike = {
_zod: {
input?: unknown;
output?: unknown;
};
};
type Zod4SchemaLike<Input = FieldValues, Output = unknown> = {
_zod: {
input: Input;
output: Output;
};
};
type Zod4Input<T extends Zod4TypeLike> = T extends {
_zod: { input?: infer I };
}
? I extends FieldValues
? I
: FieldValues
: FieldValues;
type Zod4Output<T extends Zod4TypeLike> = T extends {
_zod: { output?: infer O };
}
? O
: unknown;

// some type magic to make versions pre-3.25.0 still work
type IsUnresolved<T> = PropertyKey extends keyof T ? true : false;
Expand Down Expand Up @@ -197,26 +221,21 @@ export function zodResolver<Input extends FieldValues, Context, Output>(
resolverOptions: RawResolverOptions,
): Resolver<Input, Context, Input>;
// the Zod 4 overloads need to be generic for complicated reasons
export function zodResolver<
Input extends FieldValues,
Context,
Output,
T extends z4.$ZodType<Output, Input> = z4.$ZodType<Output, Input>,
>(
export function zodResolver<Context, T extends Zod4TypeLike>(
schema: T,
schemaOptions?: Zod4ParseParams, // already partial
resolverOptions?: NonRawResolverOptions,
): Resolver<z4.input<T>, Context, z4.output<T>>;
export function zodResolver<
Input extends FieldValues,
Context,
Output,
T extends z4.$ZodType<Output, Input> = z4.$ZodType<Output, Input>,
>(
schema: z4.$ZodType<Output, Input>,
): Resolver<Zod4Input<T>, Context, Zod4Output<T>>;
export function zodResolver<Input extends FieldValues, Context, Output>(
schema: Zod4SchemaLike<Input, Output>,
schemaOptions?: Zod4ParseParams, // already partial
resolverOptions?: NonRawResolverOptions,
): Resolver<Input, Context, Output>;
export function zodResolver<Input extends FieldValues, Context, Output>(
schema: Zod4SchemaLike<Input, Output>,
schemaOptions: Zod4ParseParams | undefined, // already partial
resolverOptions: RawResolverOptions,
): Resolver<z4.input<T>, Context, z4.input<T>>;
): Resolver<Input, Context, Input>;
/**
* Creates a resolver function for react-hook-form that validates form data using a Zod schema
* @param {z3.ZodSchema<Input>} schema - The Zod schema used to validate the form data
Expand Down