zod 3.20.0-beta.0 → 3.20.1

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/README.md CHANGED
@@ -89,8 +89,9 @@
89
89
  - [Cyclical data](#cyclical-objects)
90
90
  - [Promises](#promises)
91
91
  - [Instanceof](#instanceof)
92
- - [Function schemas](#function-schemas)
92
+ - [Functions](#functions)
93
93
  - [Preprocess](#preprocess)
94
+ - [Custom](#custom-schemas)
94
95
  - [Schema methods](#schema-methods)
95
96
  - [.parse](#parse)
96
97
  - [.parseAsync](#parseasync)
@@ -134,7 +135,7 @@ Some other great aspects:
134
135
  - Zero dependencies
135
136
  - Works in Node.js and all modern browsers
136
137
  - Tiny: 8kb minified + zipped
137
- - Immutable: methods (i.e. `.optional()`) return a new instance
138
+ - Immutable: methods (e.g. `.optional()`) return a new instance
138
139
  - Concise, chainable interface
139
140
  - Functional approach: [parse, don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/)
140
141
  - Works with plain JavaScript too! You don't need to use TypeScript.
@@ -358,6 +359,8 @@ There are a growing number of tools that are built atop or support Zod natively!
358
359
  - [`zod-formik-adapter`](https://github.com/robertLichtnow/zod-formik-adapter): A community-maintained Formik adapter for Zod.
359
360
  - [`react-zorm`](https://github.com/esamattis/react-zorm): Standalone `<form>` generation and validation for React using Zod.
360
361
  - [`zodix`](https://github.com/rileytomasek/zodix): Zod utilities for FormData and URLSearchParams in Remix loaders and actions.
362
+ - [`formik-validator-zod`](https://github.com/glazy/formik-validator-zod): Formik-compliant validator library that simplifies using Zod with Formik.
363
+ - [`zod-i18n-map`](https://github.com/aiji42/zod-i18n): Useful for translating Zod error messages.
361
364
 
362
365
  #### Zod to X
363
366
 
@@ -392,6 +395,7 @@ There are a growing number of tools that are built atop or support Zod natively!
392
395
  - [`slonik`](https://github.com/gajus/slonik/tree/gajus/add-zod-validation-backwards-compatible#runtime-validation-and-static-type-inference): Node.js Postgres client with strong Zod integration.
393
396
  - [`soly`](https://github.com/mdbetancourt/soly): Create CLI applications with zod.
394
397
  - [`zod-xlsx`](https://github.com/sidwebworks/zod-xlsx): A xlsx based resource validator using Zod schemas.
398
+ - [`znv`](https://github.com/lostfictions/znv): Type-safe environment parsing and validation for Node.js with Zod schemas
395
399
 
396
400
  ## Installation
397
401
 
@@ -1530,7 +1534,7 @@ TestSchema.parse(new Test()); // passes
1530
1534
  TestSchema.parse("blob"); // throws
1531
1535
  ```
1532
1536
 
1533
- ## Function schemas
1537
+ ## Functions
1534
1538
 
1535
1539
  Zod also lets you define "function schemas". This makes it easy to validate the inputs and outputs of a function without intermixing your validation code and "business logic".
1536
1540
 
@@ -1550,6 +1554,7 @@ const myFunction = z
1550
1554
  .function()
1551
1555
  .args(z.string(), z.number()) // accepts an arbitrary number of arguments
1552
1556
  .returns(z.boolean());
1557
+
1553
1558
  type myFunction = z.infer<typeof myFunction>;
1554
1559
  // => (arg0: string, arg1: number)=>boolean
1555
1560
  ```
@@ -1591,8 +1596,9 @@ const myFunction = z
1591
1596
  .function()
1592
1597
  .args(z.string())
1593
1598
  .implement((arg) => {
1594
- return [arg.length]; //
1599
+ return [arg.length];
1595
1600
  });
1601
+
1596
1602
  myFunction; // (arg: string)=>number[]
1597
1603
  ```
1598
1604
 
@@ -1613,6 +1619,8 @@ myFunction.returnType();
1613
1619
 
1614
1620
  ## Preprocess
1615
1621
 
1622
+ > Zod now supports primitive coercion without the need for `.preprocess()`. See the [coercion docs](#coercion-for-primitives) for more information.
1623
+
1616
1624
  Typically Zod operates under a "parse then transform" paradigm. Zod validates the input first, then passes it through a chain of transformation functions. (For more information about transforms, read the [.transform docs](#transform).)
1617
1625
 
1618
1626
  But sometimes you want to apply some transform to the input _before_ parsing happens. A common use case: type coercion. Zod enables this with the `z.preprocess()`.
@@ -1623,6 +1631,22 @@ const castToString = z.preprocess((val) => String(val), z.string());
1623
1631
 
1624
1632
  This returns a `ZodEffects` instance. `ZodEffects` is a wrapper class that contains all logic pertaining to preprocessing, refinements, and transforms.
1625
1633
 
1634
+ ## Custom schemas
1635
+
1636
+ You can create a Zod schema for any TypeScript type by using `z.custom()`. This is useful for creating schemas for types that are not supported by Zod out of the box, such as template string literals.
1637
+
1638
+ ```ts
1639
+ const px = z.custom<`${number}px`>((val) => /^\d+px$/.test(val));
1640
+ px.parse("100px"); // pass
1641
+ px.parse("100vw"); // fail
1642
+ ```
1643
+
1644
+ If you don't provide a validation function, Zod will allow any value. This can be dangerous!
1645
+
1646
+ ```ts
1647
+ z.custom<{ arg: string }>(); // performs no validation
1648
+ ```
1649
+
1626
1650
  ## Schema methods
1627
1651
 
1628
1652
  All Zod schemas contain certain methods.
@@ -1637,6 +1661,7 @@ Given any Zod schema, you can call its `.parse` method to check `data` is valid.
1637
1661
 
1638
1662
  ```ts
1639
1663
  const stringSchema = z.string();
1664
+
1640
1665
  stringSchema.parse("fish"); // => returns "fish"
1641
1666
  stringSchema.parse(12); // throws Error('Non-string type: number');
1642
1667
  ```
@@ -1740,7 +1765,7 @@ type RefineParams = {
1740
1765
  For advanced cases, the second argument can also be a function that returns `RefineParams`/
1741
1766
 
1742
1767
  ```ts
1743
- z.string().refine(
1768
+ const longString = z.string().refine(
1744
1769
  (val) => val.length > 10,
1745
1770
  (val) => ({ message: `${val} is not more than 10 characters` })
1746
1771
  );
@@ -1757,8 +1782,9 @@ const passwordForm = z
1757
1782
  .refine((data) => data.password === data.confirm, {
1758
1783
  message: "Passwords don't match",
1759
1784
  path: ["confirm"], // path of error
1760
- })
1761
- .parse({ password: "asdf", confirm: "qwer" });
1785
+ });
1786
+
1787
+ passwordForm.parse({ password: "asdf", confirm: "qwer" });
1762
1788
  ```
1763
1789
 
1764
1790
  Because you provided a `path` parameter, the resulting error will be:
@@ -1798,7 +1824,6 @@ z.string()
1798
1824
 
1799
1825
  <!-- Note that the `path` is set to `["confirm"]` , so you can easily display this error underneath the "Confirm password" textbox.
1800
1826
 
1801
-
1802
1827
  ```ts
1803
1828
  const allForms = z.object({ passwordForm }).parse({
1804
1829
  passwordForm: {
@@ -1875,12 +1900,40 @@ const schema = z.number().superRefine((val, ctx) => {
1875
1900
  });
1876
1901
  ```
1877
1902
 
1903
+ #### Type refinements
1904
+
1905
+ If you provide a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) to `.refine()` or `superRefine()`, the resulting type will be narrowed down to your predicate's type. This is useful if you are mixing multiple chained refinements and transformations:
1906
+
1907
+ ```ts
1908
+ const schema = z
1909
+ .object({
1910
+ first: z.string(),
1911
+ second: z.number(),
1912
+ })
1913
+ .nullable()
1914
+ .superRefine((arg, ctx): arg is { first: string; second: number } => {
1915
+ if (!arg) {
1916
+ ctx.addIssue({
1917
+ code: z.ZodIssueCode.custom, // customize your issue
1918
+ message: "object should exist",
1919
+ });
1920
+ return false;
1921
+ }
1922
+ return true;
1923
+ })
1924
+ // here, TS knows that arg is not null
1925
+ .refine((arg) => arg.first === "bob", "`first` is not `bob`!");
1926
+ ```
1927
+
1928
+ > ⚠️ You must **still** call `ctx.addIssue()` if using `superRefine()` with a type predicate function. Otherwise the refinement won't be validated.
1929
+
1878
1930
  ### `.transform`
1879
1931
 
1880
1932
  To transform data after parsing, use the `transform` method.
1881
1933
 
1882
1934
  ```ts
1883
1935
  const stringToNumber = z.string().transform((val) => val.length);
1936
+
1884
1937
  stringToNumber.parse("string"); // => 6
1885
1938
  ```
1886
1939
 
@@ -1927,7 +1980,8 @@ const Strings = z.string().transform((val, ctx) => {
1927
1980
  Transforms and refinements can be interleaved. These will be executed in the order they are declared.
1928
1981
 
1929
1982
  ```ts
1930
- z.string()
1983
+ const nameToGreeting = z
1984
+ .string()
1931
1985
  .transform((val) => val.toUpperCase())
1932
1986
  .refine((val) => val.length > 15)
1933
1987
  .transform((val) => `Hello ${val}`)
@@ -2038,7 +2092,7 @@ z.string().optional().nullable();
2038
2092
  A convenience method that returns an array schema for the given type:
2039
2093
 
2040
2094
  ```ts
2041
- const nullableString = z.string().array(); // string[]
2095
+ const stringArray = z.string().array(); // string[]
2042
2096
 
2043
2097
  // equivalent to
2044
2098
  z.array(z.string());
@@ -2060,7 +2114,7 @@ z.promise(z.string());
2060
2114
  A convenience method for union types.
2061
2115
 
2062
2116
  ```ts
2063
- z.string().or(z.number()); // string | number
2117
+ const stringOrNumber = z.string().or(z.number()); // string | number
2064
2118
 
2065
2119
  // equivalent to
2066
2120
  z.union([z.string(), z.number()]);
@@ -2071,7 +2125,9 @@ z.union([z.string(), z.number()]);
2071
2125
  A convenience method for creating intersection types.
2072
2126
 
2073
2127
  ```ts
2074
- z.object({ name: z.string() }).and(z.object({ age: z.number() })); // { name: string } & { age: number }
2128
+ const nameAndAge = z
2129
+ .object({ name: z.string() })
2130
+ .and(z.object({ age: z.number() })); // { name: string } & { age: number }
2075
2131
 
2076
2132
  // equivalent to
2077
2133
  z.intersection(z.object({ name: z.string() }), z.object({ age: z.number() }));
@@ -2151,7 +2207,7 @@ type inferred = z.infer<typeof stringToNumber>; // number
2151
2207
 
2152
2208
  ### Writing generic functions
2153
2209
 
2154
- When attempting to write a functions that accepts a Zod schemas as an input, it's common to try something like this:
2210
+ When attempting to write a function that accepts a Zod schema as an input, it's common to try something like this:
2155
2211
 
2156
2212
  ```ts
2157
2213
  function makeSchemaOptional<T>(schema: z.ZodType<T>) {
@@ -2166,7 +2222,7 @@ const arg = makeSchemaOptional(z.string());
2166
2222
  arg.unwrap();
2167
2223
  ```
2168
2224
 
2169
- A better approach is for the generate parameter to refer to _the schema as a whole_.
2225
+ A better approach is for the generic parameter to refer to _the schema as a whole_.
2170
2226
 
2171
2227
  ```ts
2172
2228
  function makeSchemaOptional<T extends z.ZodTypeAny>(schema: T) {
package/lib/ZodError.d.ts CHANGED
@@ -82,12 +82,14 @@ export interface ZodTooSmallIssue extends ZodIssueBase {
82
82
  code: typeof ZodIssueCode.too_small;
83
83
  minimum: number;
84
84
  inclusive: boolean;
85
+ exact?: boolean;
85
86
  type: "array" | "string" | "number" | "set" | "date";
86
87
  }
87
88
  export interface ZodTooBigIssue extends ZodIssueBase {
88
89
  code: typeof ZodIssueCode.too_big;
89
90
  maximum: number;
90
91
  inclusive: boolean;
92
+ exact?: boolean;
91
93
  type: "array" | "string" | "number" | "set" | "date";
92
94
  }
93
95
  export interface ZodInvalidIntersectionTypesIssue extends ZodIssueBase {
package/lib/external.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from "./errors";
2
2
  export * from "./helpers/parseUtil";
3
3
  export * from "./helpers/typeAliases";
4
- export { getParsedType, ZodParsedType } from "./helpers/util";
4
+ export * from "./helpers/util";
5
5
  export * from "./types";
6
6
  export * from "./ZodError";
package/lib/external.js CHANGED
@@ -10,12 +10,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
10
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.ZodParsedType = exports.getParsedType = void 0;
14
13
  __exportStar(require("./errors"), exports);
15
14
  __exportStar(require("./helpers/parseUtil"), exports);
16
15
  __exportStar(require("./helpers/typeAliases"), exports);
17
- var util_1 = require("./helpers/util");
18
- Object.defineProperty(exports, "getParsedType", { enumerable: true, get: function () { return util_1.getParsedType; } });
19
- Object.defineProperty(exports, "ZodParsedType", { enumerable: true, get: function () { return util_1.ZodParsedType; } });
16
+ __exportStar(require("./helpers/util"), exports);
20
17
  __exportStar(require("./types"), exports);
21
18
  __exportStar(require("./ZodError"), exports);
package/lib/index.mjs CHANGED
@@ -311,25 +311,41 @@ const errorMap = (issue, _ctx) => {
311
311
  break;
312
312
  case ZodIssueCode.too_small:
313
313
  if (issue.type === "array")
314
- message = `Array must contain ${issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`;
314
+ message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`;
315
315
  else if (issue.type === "string")
316
- message = `String must contain ${issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`;
316
+ message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`;
317
317
  else if (issue.type === "number")
318
- message = `Number must be greater than ${issue.inclusive ? `or equal to ` : ``}${issue.minimum}`;
318
+ message = `Number must be ${issue.exact
319
+ ? `exactly equal to `
320
+ : issue.inclusive
321
+ ? `greater than or equal to `
322
+ : `greater than `}${issue.minimum}`;
319
323
  else if (issue.type === "date")
320
- message = `Date must be greater than ${issue.inclusive ? `or equal to ` : ``}${new Date(issue.minimum)}`;
324
+ message = `Date must be ${issue.exact
325
+ ? `exactly equal to `
326
+ : issue.inclusive
327
+ ? `greater than or equal to `
328
+ : `greater than `}${new Date(issue.minimum)}`;
321
329
  else
322
330
  message = "Invalid input";
323
331
  break;
324
332
  case ZodIssueCode.too_big:
325
333
  if (issue.type === "array")
326
- message = `Array must contain ${issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`;
334
+ message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`;
327
335
  else if (issue.type === "string")
328
- message = `String must contain ${issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`;
336
+ message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`;
329
337
  else if (issue.type === "number")
330
- message = `Number must be less than ${issue.inclusive ? `or equal to ` : ``}${issue.maximum}`;
338
+ message = `Number must be ${issue.exact
339
+ ? `exactly`
340
+ : issue.inclusive
341
+ ? `less than or equal to`
342
+ : `less than`} ${issue.maximum}`;
331
343
  else if (issue.type === "date")
332
- message = `Date must be smaller than ${issue.inclusive ? `or equal to ` : ``}${new Date(issue.maximum)}`;
344
+ message = `Date must be ${issue.exact
345
+ ? `exactly`
346
+ : issue.inclusive
347
+ ? `smaller than or equal to`
348
+ : `smaller than`} ${new Date(issue.maximum)}`;
333
349
  else
334
350
  message = "Invalid input";
335
351
  break;
@@ -510,7 +526,6 @@ class ZodType {
510
526
  constructor(def) {
511
527
  /** Alias of safeParseAsync */
512
528
  this.spa = this.safeParseAsync;
513
- this.superRefine = this._refinement;
514
529
  this._def = def;
515
530
  this.parse = this.parse.bind(this);
516
531
  this.safeParse = this.safeParse.bind(this);
@@ -682,6 +697,9 @@ class ZodType {
682
697
  effect: { type: "refinement", refinement },
683
698
  });
684
699
  }
700
+ superRefine(refinement) {
701
+ return this._refinement(refinement);
702
+ }
685
703
  optional() {
686
704
  return ZodOptional.create(this);
687
705
  }
@@ -756,7 +774,7 @@ const uuidRegex = /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-
756
774
  // old version: too slow, didn't support unicode
757
775
  // const emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
758
776
  // eslint-disable-next-line
759
- const emailRegex = /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
777
+ const emailRegex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@((?!-)([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})(?<!-)$/i;
760
778
  // interface IsDateStringOptions extends StringDateOptions {
761
779
  /**
762
780
  * Match any configuration
@@ -835,6 +853,7 @@ class ZodString extends ZodType {
835
853
  minimum: check.value,
836
854
  type: "string",
837
855
  inclusive: true,
856
+ exact: false,
838
857
  message: check.message,
839
858
  });
840
859
  status.dirty();
@@ -848,11 +867,40 @@ class ZodString extends ZodType {
848
867
  maximum: check.value,
849
868
  type: "string",
850
869
  inclusive: true,
870
+ exact: false,
851
871
  message: check.message,
852
872
  });
853
873
  status.dirty();
854
874
  }
855
875
  }
876
+ else if (check.kind === "length") {
877
+ const tooBig = input.data.length > check.value;
878
+ const tooSmall = input.data.length < check.value;
879
+ if (tooBig || tooSmall) {
880
+ ctx = this._getOrReturnCtx(input, ctx);
881
+ if (tooBig) {
882
+ addIssueToContext(ctx, {
883
+ code: ZodIssueCode.too_big,
884
+ maximum: check.value,
885
+ type: "string",
886
+ inclusive: true,
887
+ exact: true,
888
+ message: check.message,
889
+ });
890
+ }
891
+ else if (tooSmall) {
892
+ addIssueToContext(ctx, {
893
+ code: ZodIssueCode.too_small,
894
+ minimum: check.value,
895
+ type: "string",
896
+ inclusive: true,
897
+ exact: true,
898
+ message: check.message,
899
+ });
900
+ }
901
+ status.dirty();
902
+ }
903
+ }
856
904
  else if (check.kind === "email") {
857
905
  if (!emailRegex.test(input.data)) {
858
906
  ctx = this._getOrReturnCtx(input, ctx);
@@ -1027,9 +1075,13 @@ class ZodString extends ZodType {
1027
1075
  });
1028
1076
  }
1029
1077
  length(len, message) {
1030
- return this.min(len, message).max(len, message);
1078
+ return this._addCheck({
1079
+ kind: "length",
1080
+ value: len,
1081
+ ...errorUtil.errToObj(message),
1082
+ });
1031
1083
  }
1032
- isDatetime() {
1084
+ get isDatetime() {
1033
1085
  return !!this._def.checks.find((ch) => ch.kind === "datetime");
1034
1086
  }
1035
1087
  get isEmail() {
@@ -1130,6 +1182,7 @@ class ZodNumber extends ZodType {
1130
1182
  minimum: check.value,
1131
1183
  type: "number",
1132
1184
  inclusive: check.inclusive,
1185
+ exact: false,
1133
1186
  message: check.message,
1134
1187
  });
1135
1188
  status.dirty();
@@ -1146,6 +1199,7 @@ class ZodNumber extends ZodType {
1146
1199
  maximum: check.value,
1147
1200
  type: "number",
1148
1201
  inclusive: check.inclusive,
1202
+ exact: false,
1149
1203
  message: check.message,
1150
1204
  });
1151
1205
  status.dirty();
@@ -1376,6 +1430,7 @@ class ZodDate extends ZodType {
1376
1430
  code: ZodIssueCode.too_small,
1377
1431
  message: check.message,
1378
1432
  inclusive: true,
1433
+ exact: false,
1379
1434
  minimum: check.value,
1380
1435
  type: "date",
1381
1436
  });
@@ -1389,6 +1444,7 @@ class ZodDate extends ZodType {
1389
1444
  code: ZodIssueCode.too_big,
1390
1445
  message: check.message,
1391
1446
  inclusive: true,
1447
+ exact: false,
1392
1448
  maximum: check.value,
1393
1449
  type: "date",
1394
1450
  });
@@ -1598,6 +1654,22 @@ class ZodArray extends ZodType {
1598
1654
  });
1599
1655
  return INVALID;
1600
1656
  }
1657
+ if (def.exactLength !== null) {
1658
+ const tooBig = ctx.data.length > def.exactLength.value;
1659
+ const tooSmall = ctx.data.length < def.exactLength.value;
1660
+ if (tooBig || tooSmall) {
1661
+ addIssueToContext(ctx, {
1662
+ code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small,
1663
+ minimum: (tooSmall ? def.exactLength.value : undefined),
1664
+ maximum: (tooBig ? def.exactLength.value : undefined),
1665
+ type: "array",
1666
+ inclusive: true,
1667
+ exact: true,
1668
+ message: def.exactLength.message,
1669
+ });
1670
+ status.dirty();
1671
+ }
1672
+ }
1601
1673
  if (def.minLength !== null) {
1602
1674
  if (ctx.data.length < def.minLength.value) {
1603
1675
  addIssueToContext(ctx, {
@@ -1605,6 +1677,7 @@ class ZodArray extends ZodType {
1605
1677
  minimum: def.minLength.value,
1606
1678
  type: "array",
1607
1679
  inclusive: true,
1680
+ exact: false,
1608
1681
  message: def.minLength.message,
1609
1682
  });
1610
1683
  status.dirty();
@@ -1617,6 +1690,7 @@ class ZodArray extends ZodType {
1617
1690
  maximum: def.maxLength.value,
1618
1691
  type: "array",
1619
1692
  inclusive: true,
1693
+ exact: false,
1620
1694
  message: def.maxLength.message,
1621
1695
  });
1622
1696
  status.dirty();
@@ -1650,7 +1724,10 @@ class ZodArray extends ZodType {
1650
1724
  });
1651
1725
  }
1652
1726
  length(len, message) {
1653
- return this.min(len, message).max(len, message);
1727
+ return new ZodArray({
1728
+ ...this._def,
1729
+ exactLength: { value: len, message: errorUtil.toString(message) },
1730
+ });
1654
1731
  }
1655
1732
  nonempty(message) {
1656
1733
  return this.min(1, message);
@@ -1661,6 +1738,7 @@ ZodArray.create = (schema, params) => {
1661
1738
  type: schema,
1662
1739
  minLength: null,
1663
1740
  maxLength: null,
1741
+ exactLength: null,
1664
1742
  typeName: ZodFirstPartyTypeKind.ZodArray,
1665
1743
  ...processCreateParams(params),
1666
1744
  });
@@ -2337,6 +2415,7 @@ class ZodTuple extends ZodType {
2337
2415
  code: ZodIssueCode.too_small,
2338
2416
  minimum: this._def.items.length,
2339
2417
  inclusive: true,
2418
+ exact: false,
2340
2419
  type: "array",
2341
2420
  });
2342
2421
  return INVALID;
@@ -2347,6 +2426,7 @@ class ZodTuple extends ZodType {
2347
2426
  code: ZodIssueCode.too_big,
2348
2427
  maximum: this._def.items.length,
2349
2428
  inclusive: true,
2429
+ exact: false,
2350
2430
  type: "array",
2351
2431
  });
2352
2432
  status.dirty();
@@ -2522,6 +2602,7 @@ class ZodSet extends ZodType {
2522
2602
  minimum: def.minSize.value,
2523
2603
  type: "set",
2524
2604
  inclusive: true,
2605
+ exact: false,
2525
2606
  message: def.minSize.message,
2526
2607
  });
2527
2608
  status.dirty();
@@ -2534,6 +2615,7 @@ class ZodSet extends ZodType {
2534
2615
  maximum: def.maxSize.value,
2535
2616
  type: "set",
2536
2617
  inclusive: true,
2618
+ exact: false,
2537
2619
  message: def.maxSize.message,
2538
2620
  });
2539
2621
  status.dirty();
@@ -3084,18 +3166,16 @@ class ZodCatch extends ZodType {
3084
3166
  });
3085
3167
  if (isAsync(result)) {
3086
3168
  return result.then((result) => {
3087
- const defaultValue = this._def.defaultValue();
3088
3169
  return {
3089
3170
  status: "valid",
3090
- value: result.status === "valid" ? result.value : defaultValue,
3171
+ value: result.status === "valid" ? result.value : this._def.defaultValue(),
3091
3172
  };
3092
3173
  });
3093
3174
  }
3094
3175
  else {
3095
- const defaultValue = this._def.defaultValue();
3096
3176
  return {
3097
3177
  status: "valid",
3098
- value: result.status === "valid" ? result.value : defaultValue,
3178
+ value: result.status === "valid" ? result.value : this._def.defaultValue(),
3099
3179
  };
3100
3180
  }
3101
3181
  }
@@ -3259,13 +3339,9 @@ var ZodFirstPartyTypeKind;
3259
3339
  ZodFirstPartyTypeKind["ZodBranded"] = "ZodBranded";
3260
3340
  ZodFirstPartyTypeKind["ZodPipeline"] = "ZodPipeline";
3261
3341
  })(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
3262
- // new approach that works for abstract classes
3263
- // but required TS 4.4+
3264
- // abstract class Class {
3265
- // constructor(..._: any[]) {}
3266
- // }
3267
- // const instanceOfType = <T extends typeof Class>(
3268
- const instanceOfType = (cls, params = {
3342
+ const instanceOfType = (
3343
+ // const instanceOfType = <T extends new (...args: any[]) => any>(
3344
+ cls, params = {
3269
3345
  message: `Input not instance of ${cls.name}`,
3270
3346
  }) => custom((data) => data instanceof cls, params, true);
3271
3347
  const stringType = ZodString.create;
@@ -3316,8 +3392,6 @@ const NEVER = INVALID;
3316
3392
 
3317
3393
  var mod = /*#__PURE__*/Object.freeze({
3318
3394
  __proto__: null,
3319
- getParsedType: getParsedType,
3320
- ZodParsedType: ZodParsedType,
3321
3395
  defaultErrorMap: errorMap,
3322
3396
  setErrorMap: setErrorMap,
3323
3397
  getErrorMap: getErrorMap,
@@ -3332,6 +3406,9 @@ var mod = /*#__PURE__*/Object.freeze({
3332
3406
  isDirty: isDirty,
3333
3407
  isValid: isValid,
3334
3408
  isAsync: isAsync,
3409
+ get util () { return util; },
3410
+ ZodParsedType: ZodParsedType,
3411
+ getParsedType: getParsedType,
3335
3412
  ZodType: ZodType,
3336
3413
  ZodString: ZodString,
3337
3414
  ZodNumber: ZodNumber,
@@ -3422,4 +3499,4 @@ var mod = /*#__PURE__*/Object.freeze({
3422
3499
  ZodError: ZodError
3423
3500
  });
3424
3501
 
3425
- export { BRAND, DIRTY, EMPTY_PATH, INVALID, NEVER, OK, ParseStatus, ZodType as Schema, ZodAny, ZodArray, ZodBigInt, ZodBoolean, ZodBranded, ZodCatch, ZodDate, ZodDefault, ZodDiscriminatedUnion, ZodEffects, ZodEnum, ZodError, ZodFirstPartyTypeKind, ZodFunction, ZodIntersection, ZodIssueCode, ZodLazy, ZodLiteral, ZodMap, ZodNaN, ZodNativeEnum, ZodNever, ZodNull, ZodNullable, ZodNumber, ZodObject, ZodOptional, ZodParsedType, ZodPipeline, ZodPromise, ZodRecord, ZodType as ZodSchema, ZodSet, ZodString, ZodSymbol, ZodEffects as ZodTransformer, ZodTuple, ZodType, ZodUndefined, ZodUnion, ZodUnknown, ZodVoid, addIssueToContext, anyType as any, arrayType as array, bigIntType as bigint, booleanType as boolean, coerce, custom, dateType as date, mod as default, errorMap as defaultErrorMap, discriminatedUnionType as discriminatedUnion, effectsType as effect, enumType as enum, functionType as function, getErrorMap, getParsedType, instanceOfType as instanceof, intersectionType as intersection, isAborted, isAsync, isDirty, isValid, late, lazyType as lazy, literalType as literal, makeIssue, mapType as map, nanType as nan, nativeEnumType as nativeEnum, neverType as never, nullType as null, nullableType as nullable, numberType as number, objectType as object, objectUtil, oboolean, onumber, optionalType as optional, ostring, pipelineType as pipeline, preprocessType as preprocess, promiseType as promise, quotelessJson, recordType as record, setType as set, setErrorMap, strictObjectType as strictObject, stringType as string, symbolType as symbol, effectsType as transformer, tupleType as tuple, undefinedType as undefined, unionType as union, unknownType as unknown, voidType as void, mod as z };
3502
+ export { BRAND, DIRTY, EMPTY_PATH, INVALID, NEVER, OK, ParseStatus, ZodType as Schema, ZodAny, ZodArray, ZodBigInt, ZodBoolean, ZodBranded, ZodCatch, ZodDate, ZodDefault, ZodDiscriminatedUnion, ZodEffects, ZodEnum, ZodError, ZodFirstPartyTypeKind, ZodFunction, ZodIntersection, ZodIssueCode, ZodLazy, ZodLiteral, ZodMap, ZodNaN, ZodNativeEnum, ZodNever, ZodNull, ZodNullable, ZodNumber, ZodObject, ZodOptional, ZodParsedType, ZodPipeline, ZodPromise, ZodRecord, ZodType as ZodSchema, ZodSet, ZodString, ZodSymbol, ZodEffects as ZodTransformer, ZodTuple, ZodType, ZodUndefined, ZodUnion, ZodUnknown, ZodVoid, addIssueToContext, anyType as any, arrayType as array, bigIntType as bigint, booleanType as boolean, coerce, custom, dateType as date, mod as default, errorMap as defaultErrorMap, discriminatedUnionType as discriminatedUnion, effectsType as effect, enumType as enum, functionType as function, getErrorMap, getParsedType, instanceOfType as instanceof, intersectionType as intersection, isAborted, isAsync, isDirty, isValid, late, lazyType as lazy, literalType as literal, makeIssue, mapType as map, nanType as nan, nativeEnumType as nativeEnum, neverType as never, nullType as null, nullableType as nullable, numberType as number, objectType as object, objectUtil, oboolean, onumber, optionalType as optional, ostring, pipelineType as pipeline, preprocessType as preprocess, promiseType as promise, quotelessJson, recordType as record, setType as set, setErrorMap, strictObjectType as strictObject, stringType as string, symbolType as symbol, effectsType as transformer, tupleType as tuple, undefinedType as undefined, unionType as union, unknownType as unknown, util, voidType as void, mod as z };