z.mini.merge() declares a schema-to-schema signature but forwards to util.extend(), which requires a plain shape. The call that typechecks throws at runtime, and the call that runs is a type error. There's no way to call it that satisfies both.
import * as m from "zod/mini";
const A = m.object({ a: m.string() });
const B = m.object({ b: m.string() });
m.merge(A, B); // typechecks; throws at runtime
m.merge(A, { b: m.string() }); // works at runtime; type error TS2353
On zod@4.4.3:
merge(schema, schema) THROWS: Invalid input to extend: expected a plain object
merge(schema, shape) : [ 'a', 'b' ]
Under tsc --noEmit --strict (5.9.3 and 7.0.2 agree):
error TS2353: Object literal may only specify known properties,
and 'b' does not exist in type 'ZodMiniObject<...>'
Cause
mini/schemas.ts:921 declares only the schema-to-schema overload, so the implementation signature isn't publicly callable:
/** @deprecated Identical to `z.extend(A, B)` */
export function merge<T extends ZodMiniObject, U extends ZodMiniObject>(
a: T,
b: U
): ZodMiniObject<util.Extend<T["shape"], U["shape"]>, T["_zod"]["config"]>;
export function merge(schema: ZodMiniObject, shape: any): ZodMiniObject {
return util.extend(schema, shape);
}
util.extend guards on isPlainObject and throws (core/util.ts:659).
Mini only. Classic calls util.merge (classic/schemas.ts:1555) and A.merge(B) works. Present since the Zod 4 commit 8592854, unchanged in 4.4.3. mini/tests/object.test.ts has no merge case.
Two fixes, not equivalent
Correcting the overload to (schema, shape) matches the @deprecated Identical to z.extend(A, B) note above it, with no runtime change. It breaks merge(A, B) at compile time, but that call already throws, so the failure just surfaces earlier.
Switching the body to util.merge makes the advertised signature true, matches classic and Zod 3, and picks up b's catchall. It breaks JS callers passing a plain shape, which is the form that works today. Not silently: checks: b._zod.def.checks ?? [] (core/util.ts:713) is eager, so it throws TypeError: Cannot read properties of undefined (reading 'def') at the call. Still a runtime change to a shipped API.
merge is deprecated and absent from api.mdx, so the first is the conservative read. Which would you prefer? Happy to open a PR with a test either way.
z.mini.merge()declares a schema-to-schema signature but forwards toutil.extend(), which requires a plain shape. The call that typechecks throws at runtime, and the call that runs is a type error. There's no way to call it that satisfies both.On
zod@4.4.3:Under
tsc --noEmit --strict(5.9.3 and 7.0.2 agree):Cause
mini/schemas.ts:921declares only the schema-to-schema overload, so the implementation signature isn't publicly callable:util.extendguards onisPlainObjectand throws (core/util.ts:659).Mini only. Classic calls
util.merge(classic/schemas.ts:1555) andA.merge(B)works. Present since the Zod 4 commit 8592854, unchanged in 4.4.3.mini/tests/object.test.tshas nomergecase.Two fixes, not equivalent
Correcting the overload to
(schema, shape)matches the@deprecated Identical to z.extend(A, B)note above it, with no runtime change. It breaksmerge(A, B)at compile time, but that call already throws, so the failure just surfaces earlier.Switching the body to
util.mergemakes the advertised signature true, matches classic and Zod 3, and picks upb'scatchall. It breaks JS callers passing a plain shape, which is the form that works today. Not silently:checks: b._zod.def.checks ?? [](core/util.ts:713) is eager, so it throwsTypeError: Cannot read properties of undefined (reading 'def')at the call. Still a runtime change to a shipped API.mergeis deprecated and absent fromapi.mdx, so the first is the conservative read. Which would you prefer? Happy to open a PR with a test either way.