zod 4.3.0-canary.20251222T205904 → 4.3.0-canary.20251223T023855

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod",
3
- "version": "4.3.0-canary.20251222T205904",
3
+ "version": "4.3.0-canary.20251223T023855",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Colin McDonnell <zod@colinhacks.com>",
@@ -435,6 +435,20 @@ test("extend() should have power to override existing key", () => {
435
435
  expectTypeOf<PersonWithNumberAsLastName>().toEqualTypeOf<{ firstName: string; lastName: number }>();
436
436
  });
437
437
 
438
+ test("safeExtend() should have power to override existing key", () => {
439
+ const PersonWithMinLastName = personToExtend.safeExtend({
440
+ lastName: z.string().min(3),
441
+ });
442
+ type PersonWithMinLastName = z.infer<typeof PersonWithMinLastName>;
443
+
444
+ const expected = { firstName: "f", lastName: "abc" };
445
+ const actual = PersonWithMinLastName.parse(expected);
446
+
447
+ expect(actual).toEqual(expected);
448
+ expect(() => PersonWithMinLastName.parse({ firstName: "f", lastName: "ab" })).toThrow();
449
+ expectTypeOf<PersonWithMinLastName>().toEqualTypeOf<{ firstName: string; lastName: string }>();
450
+ });
451
+
438
452
  test("safeExtend() maintains refinements", () => {
439
453
  const schema = z.object({ name: z.string().min(1) });
440
454
  const extended = schema.safeExtend({ name: z.string().min(2) });
@@ -338,3 +338,90 @@ test("optional with check", () => {
338
338
  }
339
339
  `);
340
340
  });
341
+
342
+ test("partial - throws error on schema with refinements", () => {
343
+ const baseSchema = z.object({
344
+ id: z.string(),
345
+ name: z.string(),
346
+ items: z.string().array(),
347
+ });
348
+
349
+ const refinedSchema = baseSchema.superRefine((val, ctx) => {
350
+ if (val.items.length === 0) {
351
+ ctx.addIssue({
352
+ message: "Must have at least one item",
353
+ code: "custom",
354
+ path: ["items"],
355
+ });
356
+ }
357
+ });
358
+
359
+ expect(() => refinedSchema.partial()).toThrow(".partial() cannot be used on object schemas containing refinements");
360
+ });
361
+
362
+ test("partial - throws error on schema with refine", () => {
363
+ const baseSchema = z.object({
364
+ password: z.string(),
365
+ confirmPassword: z.string(),
366
+ });
367
+
368
+ const refinedSchema = baseSchema.refine((data) => data.password === data.confirmPassword, {
369
+ message: "Passwords must match",
370
+ });
371
+
372
+ expect(() => refinedSchema.partial()).toThrow(".partial() cannot be used on object schemas containing refinements");
373
+ });
374
+
375
+ test("required - preserves refinements", () => {
376
+ const baseSchema = z.object({
377
+ name: z.string().optional(),
378
+ age: z.number().optional(),
379
+ });
380
+
381
+ const refinedSchema = baseSchema.superRefine((val, ctx) => {
382
+ if (val.name === "admin") {
383
+ ctx.addIssue({
384
+ message: "Name cannot be admin",
385
+ code: "custom",
386
+ path: ["name"],
387
+ });
388
+ }
389
+ });
390
+
391
+ const requiredSchema = refinedSchema.required();
392
+
393
+ // The refinement should still be applied
394
+ const result = requiredSchema.safeParse({ name: "admin", age: 25 });
395
+ expect(result.success).toBe(false);
396
+ if (!result.success) {
397
+ expect(result.error.issues[0].message).toBe("Name cannot be admin");
398
+ }
399
+
400
+ // Valid data should pass
401
+ const validResult = requiredSchema.safeParse({ name: "user", age: 25 });
402
+ expect(validResult.success).toBe(true);
403
+ });
404
+
405
+ test("required - refinement is executed on required schema", () => {
406
+ const baseSchema = z.object({
407
+ password: z.string().optional(),
408
+ confirmPassword: z.string().optional(),
409
+ });
410
+
411
+ const refinedSchema = baseSchema.refine((data) => data.password === data.confirmPassword, {
412
+ message: "Passwords must match",
413
+ });
414
+
415
+ const requiredSchema = refinedSchema.required();
416
+
417
+ // Mismatched passwords should fail refinement
418
+ const result = requiredSchema.safeParse({ password: "abc", confirmPassword: "xyz" });
419
+ expect(result.success).toBe(false);
420
+ if (!result.success) {
421
+ expect(result.error.issues[0].message).toBe("Passwords must match");
422
+ }
423
+
424
+ // Matching passwords should pass
425
+ const validResult = requiredSchema.safeParse({ password: "abc", confirmPassword: "abc" });
426
+ expect(validResult.success).toBe(true);
427
+ });
@@ -125,3 +125,78 @@ test("pick/omit/required/partial - do not allow unknown keys", () => {
125
125
  // @ts-expect-error
126
126
  expect(() => schema.partial({ $unknown: true }).safeParse({})).toThrow();
127
127
  });
128
+
129
+ test("pick - throws error on schema with refinements", () => {
130
+ const baseSchema = z.object({
131
+ id: z.string(),
132
+ name: z.string(),
133
+ items: z.string().array(),
134
+ });
135
+
136
+ const refinedSchema = baseSchema.superRefine((val, ctx) => {
137
+ if (val.items.length === 0) {
138
+ ctx.addIssue({
139
+ message: "Must have at least one item",
140
+ code: "custom",
141
+ path: ["items"],
142
+ });
143
+ }
144
+ });
145
+
146
+ expect(() => refinedSchema.pick({ name: true })).toThrow(
147
+ ".pick() cannot be used on object schemas containing refinements"
148
+ );
149
+ });
150
+
151
+ test("omit - throws error on schema with refinements", () => {
152
+ const baseSchema = z.object({
153
+ id: z.string(),
154
+ name: z.string(),
155
+ items: z.string().array(),
156
+ });
157
+
158
+ const refinedSchema = baseSchema.superRefine((val, ctx) => {
159
+ if (val.items.length === 0) {
160
+ ctx.addIssue({
161
+ message: "Must have at least one item",
162
+ code: "custom",
163
+ path: ["items"],
164
+ });
165
+ }
166
+ });
167
+
168
+ expect(() => refinedSchema.omit({ id: true })).toThrow(
169
+ ".omit() cannot be used on object schemas containing refinements"
170
+ );
171
+ });
172
+
173
+ test("pick - throws error on schema with refine", () => {
174
+ const baseSchema = z.object({
175
+ password: z.string(),
176
+ confirmPassword: z.string(),
177
+ });
178
+
179
+ const refinedSchema = baseSchema.refine((data) => data.password === data.confirmPassword, {
180
+ message: "Passwords must match",
181
+ });
182
+
183
+ expect(() => refinedSchema.pick({ password: true })).toThrow(
184
+ ".pick() cannot be used on object schemas containing refinements"
185
+ );
186
+ });
187
+
188
+ test("omit - throws error on schema with refine", () => {
189
+ const baseSchema = z.object({
190
+ password: z.string(),
191
+ confirmPassword: z.string(),
192
+ email: z.string(),
193
+ });
194
+
195
+ const refinedSchema = baseSchema.refine((data) => data.password === data.confirmPassword, {
196
+ message: "Passwords must match",
197
+ });
198
+
199
+ expect(() => refinedSchema.omit({ email: true })).toThrow(
200
+ ".omit() cannot be used on object schemas containing refinements"
201
+ );
202
+ });
@@ -308,7 +308,7 @@ test("object utilities with recursive types", () => {
308
308
  },
309
309
  });
310
310
 
311
- // Test extend
311
+ // Test extend with new keys (extend throws when overwriting existing keys)
312
312
  const NodeOne = NodeBase.extend({
313
313
  name: z.literal("nodeOne"),
314
314
  get children() {
@@ -1,16 +1,16 @@
1
1
  import { expect, test } from "vitest";
2
2
  import * as z from "zod/v4";
3
3
 
4
- test("extend chaining preserves and overrides properties", () => {
4
+ test("safeExtend chaining preserves and overrides properties", () => {
5
5
  const schema1 = z.object({
6
6
  email: z.string(),
7
7
  });
8
8
 
9
- const schema2 = schema1.extend({
9
+ const schema2 = schema1.safeExtend({
10
10
  email: schema1.shape.email.check(z.email()),
11
11
  });
12
12
 
13
- const schema3 = schema2.extend({
13
+ const schema3 = schema2.safeExtend({
14
14
  email: schema2.shape.email.or(z.literal("")),
15
15
  });
16
16
 
@@ -593,6 +593,12 @@ export const BIGINT_FORMAT_RANGES: Record<checks.$ZodBigIntFormats, [bigint, big
593
593
  export function pick(schema: schemas.$ZodObject, mask: Record<string, unknown>): any {
594
594
  const currDef = schema._zod.def;
595
595
 
596
+ const checks = currDef.checks;
597
+ const hasChecks = checks && checks.length > 0;
598
+ if (hasChecks) {
599
+ throw new Error(".pick() cannot be used on object schemas containing refinements");
600
+ }
601
+
596
602
  const def = mergeDefs(schema._zod.def, {
597
603
  get shape() {
598
604
  const newShape: Writeable<schemas.$ZodShape> = {};
@@ -616,6 +622,12 @@ export function pick(schema: schemas.$ZodObject, mask: Record<string, unknown>):
616
622
  export function omit(schema: schemas.$ZodObject, mask: object): any {
617
623
  const currDef = schema._zod.def;
618
624
 
625
+ const checks = currDef.checks;
626
+ const hasChecks = checks && checks.length > 0;
627
+ if (hasChecks) {
628
+ throw new Error(".omit() cannot be used on object schemas containing refinements");
629
+ }
630
+
619
631
  const def = mergeDefs(schema._zod.def, {
620
632
  get shape() {
621
633
  const newShape: Writeable<schemas.$ZodShape> = { ...schema._zod.def.shape };
@@ -662,15 +674,13 @@ export function safeExtend(schema: schemas.$ZodObject, shape: schemas.$ZodShape)
662
674
  if (!isPlainObject(shape)) {
663
675
  throw new Error("Invalid input to safeExtend: expected a plain object");
664
676
  }
665
- const def = {
666
- ...schema._zod.def,
677
+ const def = mergeDefs(schema._zod.def, {
667
678
  get shape() {
668
679
  const _shape = { ...schema._zod.def.shape, ...shape };
669
680
  assignProp(this, "shape", _shape); // self-caching
670
681
  return _shape;
671
682
  },
672
- checks: schema._zod.def.checks,
673
- } as any;
683
+ });
674
684
  return clone(schema, def) as any;
675
685
  }
676
686
 
@@ -695,6 +705,13 @@ export function partial(
695
705
  schema: schemas.$ZodObject,
696
706
  mask: object | undefined
697
707
  ): any {
708
+ const currDef = schema._zod.def;
709
+ const checks = currDef.checks;
710
+ const hasChecks = checks && checks.length > 0;
711
+ if (hasChecks) {
712
+ throw new Error(".partial() cannot be used on object schemas containing refinements");
713
+ }
714
+
698
715
  const def = mergeDefs(schema._zod.def, {
699
716
  get shape() {
700
717
  const oldShape = schema._zod.def.shape;
@@ -770,7 +787,6 @@ export function required(
770
787
  assignProp(this, "shape", shape); // self-caching
771
788
  return shape;
772
789
  },
773
- checks: [],
774
790
  });
775
791
 
776
792
  return clone(schema, def) as any;
package/v4/core/util.cjs CHANGED
@@ -384,6 +384,11 @@ exports.BIGINT_FORMAT_RANGES = {
384
384
  };
385
385
  function pick(schema, mask) {
386
386
  const currDef = schema._zod.def;
387
+ const checks = currDef.checks;
388
+ const hasChecks = checks && checks.length > 0;
389
+ if (hasChecks) {
390
+ throw new Error(".pick() cannot be used on object schemas containing refinements");
391
+ }
387
392
  const def = mergeDefs(schema._zod.def, {
388
393
  get shape() {
389
394
  const newShape = {};
@@ -404,6 +409,11 @@ function pick(schema, mask) {
404
409
  }
405
410
  function omit(schema, mask) {
406
411
  const currDef = schema._zod.def;
412
+ const checks = currDef.checks;
413
+ const hasChecks = checks && checks.length > 0;
414
+ if (hasChecks) {
415
+ throw new Error(".omit() cannot be used on object schemas containing refinements");
416
+ }
407
417
  const def = mergeDefs(schema._zod.def, {
408
418
  get shape() {
409
419
  const newShape = { ...schema._zod.def.shape };
@@ -445,15 +455,13 @@ function safeExtend(schema, shape) {
445
455
  if (!isPlainObject(shape)) {
446
456
  throw new Error("Invalid input to safeExtend: expected a plain object");
447
457
  }
448
- const def = {
449
- ...schema._zod.def,
458
+ const def = mergeDefs(schema._zod.def, {
450
459
  get shape() {
451
460
  const _shape = { ...schema._zod.def.shape, ...shape };
452
461
  assignProp(this, "shape", _shape); // self-caching
453
462
  return _shape;
454
463
  },
455
- checks: schema._zod.def.checks,
456
- };
464
+ });
457
465
  return clone(schema, def);
458
466
  }
459
467
  function merge(a, b) {
@@ -471,6 +479,12 @@ function merge(a, b) {
471
479
  return clone(a, def);
472
480
  }
473
481
  function partial(Class, schema, mask) {
482
+ const currDef = schema._zod.def;
483
+ const checks = currDef.checks;
484
+ const hasChecks = checks && checks.length > 0;
485
+ if (hasChecks) {
486
+ throw new Error(".partial() cannot be used on object schemas containing refinements");
487
+ }
474
488
  const def = mergeDefs(schema._zod.def, {
475
489
  get shape() {
476
490
  const oldShape = schema._zod.def.shape;
@@ -540,7 +554,6 @@ function required(Class, schema, mask) {
540
554
  assignProp(this, "shape", shape); // self-caching
541
555
  return shape;
542
556
  },
543
- checks: [],
544
557
  });
545
558
  return clone(schema, def);
546
559
  }
package/v4/core/util.js CHANGED
@@ -327,6 +327,11 @@ export const BIGINT_FORMAT_RANGES = {
327
327
  };
328
328
  export function pick(schema, mask) {
329
329
  const currDef = schema._zod.def;
330
+ const checks = currDef.checks;
331
+ const hasChecks = checks && checks.length > 0;
332
+ if (hasChecks) {
333
+ throw new Error(".pick() cannot be used on object schemas containing refinements");
334
+ }
330
335
  const def = mergeDefs(schema._zod.def, {
331
336
  get shape() {
332
337
  const newShape = {};
@@ -347,6 +352,11 @@ export function pick(schema, mask) {
347
352
  }
348
353
  export function omit(schema, mask) {
349
354
  const currDef = schema._zod.def;
355
+ const checks = currDef.checks;
356
+ const hasChecks = checks && checks.length > 0;
357
+ if (hasChecks) {
358
+ throw new Error(".omit() cannot be used on object schemas containing refinements");
359
+ }
350
360
  const def = mergeDefs(schema._zod.def, {
351
361
  get shape() {
352
362
  const newShape = { ...schema._zod.def.shape };
@@ -388,15 +398,13 @@ export function safeExtend(schema, shape) {
388
398
  if (!isPlainObject(shape)) {
389
399
  throw new Error("Invalid input to safeExtend: expected a plain object");
390
400
  }
391
- const def = {
392
- ...schema._zod.def,
401
+ const def = mergeDefs(schema._zod.def, {
393
402
  get shape() {
394
403
  const _shape = { ...schema._zod.def.shape, ...shape };
395
404
  assignProp(this, "shape", _shape); // self-caching
396
405
  return _shape;
397
406
  },
398
- checks: schema._zod.def.checks,
399
- };
407
+ });
400
408
  return clone(schema, def);
401
409
  }
402
410
  export function merge(a, b) {
@@ -414,6 +422,12 @@ export function merge(a, b) {
414
422
  return clone(a, def);
415
423
  }
416
424
  export function partial(Class, schema, mask) {
425
+ const currDef = schema._zod.def;
426
+ const checks = currDef.checks;
427
+ const hasChecks = checks && checks.length > 0;
428
+ if (hasChecks) {
429
+ throw new Error(".partial() cannot be used on object schemas containing refinements");
430
+ }
417
431
  const def = mergeDefs(schema._zod.def, {
418
432
  get shape() {
419
433
  const oldShape = schema._zod.def.shape;
@@ -483,7 +497,6 @@ export function required(Class, schema, mask) {
483
497
  assignProp(this, "shape", shape); // self-caching
484
498
  return shape;
485
499
  },
486
- checks: [],
487
500
  });
488
501
  return clone(schema, def);
489
502
  }