├── .eslintignore
├── .gitignore
├── .prettierrc
├── CHANGELOG.md
├── CONTRIBUTING.md
├── ERROR_HANDLING.md
├── FUNDING.yml
├── LICENSE
├── README.md
├── coverage.svg
├── jestconfig.json
├── logo.svg
├── package.json
├── src
├── index.ts
└── playground.ts
├── tsconfig.json
├── tsconfig.package.json
├── tslint.json
├── yarn-error.log
└── yarn.lock
/.eslintignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colinhacks/tozod/b3bb0cb750cb72e78f238432b9ae944d0cbf3283/.eslintignore
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | **/.DS_Store
3 | node_modules
4 | /lib
5 | /coverage
6 | .vscode
7 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 120,
3 | "trailingComma": "all",
4 | "singleQuote": true
5 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | | zod version | release notes |
4 | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
5 | | zod@1.9 | Added z.instanceof() and z.custom(). Implemented ZodSchema.array() method. |
6 | | zod@1.8 | Introduced z.void(). Major overhaul to error handling system, including the introduction of custom error maps. Wrote new [error handling guide](https://github.com/vriad/zod/blob/master/ERROR_HANDLING.md). |
7 | | zod@1.7 | Added several built-in validators to string, number, and array schemas. Calls to `.refine` now return new instance. |
8 | | zod@1.5 | Any and unknown types |
9 | | zod@1.4 | Refinement types (`.refine`), `.parse` no longer returns deep clone |
10 | | zod@1.3 | Promise schemas |
11 | | zod@1.2.6 | `.parse` accepts `unknown`, `bigint` schemas |
12 | | zod@1.2.5 | `.partial` and `.deepPartial` on object schemas |
13 | | zod@1.2.3 | Date schemas |
14 | | zod@1.2.0 | `.pick`, `.omit`, and `.extend` on object schemas |
15 | | zod@1.1.0 | Records |
16 | | zod@1.0.11 | `.nonstrict` |
17 | | zod@1.0.10 | Type assertions with `.check` |
18 | | zod@1.0.4 | Empty tuples |
19 | | zod@1.0.0 | Type assertions, literals, enums, detailed error reporting |
20 | | zod@1.0.0 | Initial release |
21 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | When it comes to open source, there are different ways you can contribute, all
2 | of which are valuable. Here's few guidelines that should help you as you prepare
3 | your contribution.
4 |
5 | ## Initial steps
6 |
7 | Before you start working on a contribution, create an issue describing what you want to build. It's possible someone else is already working on something similar, or perhaps there is a reason that feature isn't implemented. The maintainers will point you in the right direction.
8 |
9 |
19 |
20 | ## Development
21 |
22 | The following steps will get you setup to contribute changes to this repo:
23 |
24 | 1. Fork this repo.
25 |
26 | 2. Clone your forked repo: `git clone git@github.com:{your_username}/zod.git`
27 |
28 | 3. Run `yarn` to install dependencies.
29 |
30 | 4. Start playing with the code! You can do some simple experimentation in `src/playground.ts` (see `yarn play` below) or start implementing a feature right away.
31 |
32 | ### Commands
33 |
34 | **`yarn build`**
35 |
36 | - deletes `lib` and re-compiles `src` to `lib`
37 |
38 | **`yarn test`**
39 |
40 | - runs all Jest tests and generates coverage badge
41 |
42 | **`yarn testone`**
43 |
44 | - runs a single test file
45 | - example: `yarn testone src/__tests__/testFileNameHere.ts`
46 |
47 | **`yarn play`**
48 |
49 | - executes `src/playground.ts`, watches for changes. useful for experimentation
50 |
51 | ### Tests
52 |
53 | Zod uses Jest for testing. After implementing your contribution, write tests for it. Just create a new file under `src/__tests__` or add additional tests to the appropriate existing file.
54 |
55 | Before submitting your PR, run `yarn test` to make sure there are no (unintended) breaking changes.
56 |
57 | ### Documentation
58 |
59 | The Zod documentation lives in the README.md. Be sure to document any API changes you implement.
60 |
61 | ## License
62 |
63 | By contributing your code to the zod GitHub repository, you agree to
64 | license your contribution under the MIT license.
65 |
--------------------------------------------------------------------------------
/ERROR_HANDLING.md:
--------------------------------------------------------------------------------
1 | # Error Handling in Zod
2 |
3 | This guide explains Zod's internal error handling system, and the various ways you can customize it for your purposes.
4 |
5 | ## ZodError
6 |
7 | All validation errors thrown by Zod are instances of `ZodError`.
8 |
9 | ```ts
10 | class ZodError extends Error {
11 | errors: ZodSuberror[];
12 | }
13 | ```
14 |
15 | ZodError is a subclass of `Error`; if you want to place around with this class, you can create an instance like so:
16 |
17 | ```ts
18 | import * as z from 'zod';
19 |
20 | const myError = new z.ZodError([]);
21 | ```
22 |
23 | ZodErrors are just a wrapper class that contain information about the problems that occurred during validation. The list of issues are contained in the `errors` property.
24 |
25 | ## ZodSuberror
26 |
27 | `ZodSuberror` is _not_ a class. It is a [discriminated union](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions).
28 |
29 | The link above the the best way to learn about the concept. Discriminated unions are an ideal way to represent a data structures that may be one of many possible variants.
30 |
31 | Every ZodSuberror has these fields:
32 |
33 | | field | type | details |
34 | | --------- | ---------------------- | ------------------------------------------------------------------------------------------------- |
35 | | `code` | `z.ZodErrorCode` | You can access this enum with `z.ZodErrorCode`. A full breakdown of the possible values is below. |
36 | | `path` | `(string \| number)[]` | e.g, `['addresses', 0, 'line1']` |
37 | | `message` | `string` | e.g. `Invalid type. Expected string, received number.` |
38 |
39 | **However** depending on the error code, there may be additional properties as well. Here is a full breakdown of the additional fields by error code:
40 |
41 | ## ZodErrorCode
42 |
43 | | code | additional fields |
44 | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
45 | | ZodErrorCode.invalid_type | `expected: ZodParsedType` `received: ZodParsedType`
Jump to [this section](#parsedtype) for a breakdownthe possible values of ZodParsedType. |
46 | | ZodErrorCode.nonempty_array_is_empty | _no additional properties_ |
47 | | ZodErrorCode.unrecognized_keys | `keys: string[]` The list of unrecognized keys |
48 | | ZodErrorCode.invalid_union | `unionErrors: ZodError[]` The errors thrown by each element of the union. |
49 | | ZodErrorCode.invalid_literal_value | `expected: string \| number \| boolean` The literal value. |
50 | | ZodErrorCode.invalid_enum_value | `options: string[]` The set of acceptable string values for this enum. |
51 | | ZodErrorCode.invalid_arguments | `argumentsError: ZodError` This is a special error code only thrown by a wrapped function returned by `ZodFunction.implement()`. The `argumentsError` property is another ZodError containing the validation error details. |
52 | | ZodErrorCode.invalid_return_type | `returnTypeError: ZodError` This is a special error code only thrown by a wrapped function returned by `ZodFunction.implement()`. The `returnTypeError` property is another ZodError containing the validation error details. |
53 | | ZodErrorCode.invalid_date | _no additional properties_ |
54 | | ZodErrorCode.invalid_string | `validation: "url" \| "email" \| "uuid"` Which built-in string validator failed |
55 | | ZodErrorCode.too_small | `type: "string" \| "number" \| "array"` The type of the data failing validation
`minimum: number` The expected length/value.
`inclusive: boolean` Whether the minimum is included in the range of acceptable values. |
56 | | ZodErrorCode.too_big | `type: "string" \| "number" \| "array"` The type of the data failing validation
`maximum: number` The expected length/value.
`inclusive: boolean` Whether the minimum is included in the range of acceptable values. |
57 | | ZodErrorCode.custom_error | `params: { [k: string]: any }` This is the error code throw by **all custom refinements**. You are able to pass in a `params` object here that is available in your custom error maps (see [ZodErrorMap](#Customizing-errors-with-ZodErrorMap) below for details on error maps) |
58 |
59 | ## ZodParsedType
60 |
61 | This is an enum used byn Zod internally to represent the type of a parsed value. The possible values are:
62 |
63 | - `string`
64 | - `nan`
65 | - `number`
66 | - `integer`
67 | - `boolean`
68 | - `date`
69 | - `bigint`
70 | - `symbol`
71 | - `function`
72 | - `undefined`
73 | - `null`
74 | - `array`
75 | - `object`
76 | - `unknown`
77 | - `promise`
78 | - `void`
79 |
80 | ## A demonstrative example
81 |
82 | Here's a sample Person schema.
83 |
84 | ```ts
85 | const person = z.object({
86 | names: z.array(z.string()).nonempty(), // at least 1 name
87 | address: z.object({
88 | line1: z.string(),
89 | zipCode: z.number().min(10000), // American 5-digit code
90 | }),
91 | });
92 | ```
93 |
94 | Let's pass in some improperly formatted data.
95 |
96 | ```ts
97 | try {
98 | person.parse({
99 | names: ['Dave', 12], // 12 is not a string
100 | address: {
101 | line1: '123 Maple Ave',
102 | zipCode: 123, // zip code isnt 5 digits
103 | extra: 'other stuff', // unrecognized key
104 | },
105 | });
106 | } catch (err) {
107 | if (err instanceof z.ZodError) {
108 | console.log(err.errors);
109 | }
110 | }
111 | ```
112 |
113 | Here are the errors that will be printed:
114 |
115 | ```ts
116 | [
117 | {
118 | code: 'invalid_type',
119 | expected: 'string',
120 | received: 'number',
121 | path: ['names', 1],
122 | message: 'Invalid input: expected string, received number',
123 | },
124 | {
125 | code: 'unrecognized_keys',
126 | keys: ['extra'],
127 | path: ['address'],
128 | message: "Unrecognized key(s) in object: 'extra'",
129 | },
130 | {
131 | code: 'too_small',
132 | minimum: 10000,
133 | type: 'number',
134 | inclusive: true,
135 | path: ['address', 'zipCode'],
136 | message: 'Value should be greater than or equal to 10000',
137 | },
138 | ];
139 | ```
140 |
141 | As you can see three different issues were identified. Every ZodSuberror has a `code` property and additional metadata about the validation failure. For instance the `unrecognized_keys` error provides a list of the unrecognized keys detected in the input.
142 |
143 | ## Customizing errors with ZodErrorMap
144 |
145 | You can customize **all** error messages produced by Zod by providing a custom instance of ZodErrorMap to `.parse()`. Internally, Zod uses a [default error map](https://github.com/vriad/zod/blob/master/defaultErrorMap.ts) to produce all error messages.
146 |
147 | `ZodErrorMap` is a special function. It accepts two arguments: `error` and `ctx`. The return type is `{ message: string }`. Essentially the error map accepts some information about the validation that is failing and returns an appropriate error message.
148 |
149 | - `error: Omit`
150 |
151 | As mentioned above, ZodSuberror is a discriminated union.
152 |
153 | - `ctx: { defaultError: string; data: any }`
154 |
155 | - `ctx.default` is the error message generated by the default error map. If you only want to override the message for a single type of error, you can do that. Just return `defaultError` for everything
156 |
157 | - `ctx.data` contains the data that was passed into `.parse`. You can use this to customize the error message.
158 |
159 | ### A working example
160 |
161 | Let's look at a practical example of of customized error map:
162 |
163 | ```ts
164 | import * as z from 'zod';
165 |
166 | const errorMap: z.ZodErrorMap = (error, ctx) => {
167 | /*
168 |
169 | If error.message is set, that means the user is trying to
170 | override the error message. This is how method-specific
171 | error overrides work, like this:
172 |
173 | z.string().min(5, { message: "TOO SMALL 🤬" })
174 |
175 | It is a best practice to return `error.message` if it is set.
176 |
177 | */
178 | if (error.message) return { message: error.message };
179 |
180 | /*
181 | This is where you override the various error codes
182 | */
183 | switch (error.code) {
184 | case z.ZodErrorCode.invalid_type:
185 | if (error.expected === 'string') {
186 | return { message: `This ain't a string!` };
187 | }
188 | break;
189 | case z.ZodErrorCode.custom_error:
190 | // produce a custom message using error.params
191 | // error.params won't be set unless you passed
192 | // a `params` arguments into a custom validator
193 | const params = error.params || {};
194 | if (params.myField) {
195 | return { message: `Bad input: ${params.myField}` };
196 | }
197 | break;
198 | }
199 |
200 | // fall back to default message!
201 | return { message: ctx.defaultError };
202 | };
203 |
204 | z.string().parse(12, { errorMap });
205 |
206 | /* throws:
207 | ZodError {
208 | errors: [{
209 | code: "invalid_type",
210 | path: [],
211 | message: "This ain't a string!",
212 | expected: "string",
213 | received: "number",
214 | }]
215 | }
216 | */
217 | ```
218 |
219 | ## Error handling for forms
220 |
221 | If you're using Zod to validate the inputs from a web form, there is a convenient way to "flatten" a ZodError to a format that can be easily displayed to the end user.
222 |
223 | Consider this example of a simple signup form:
224 |
225 | ```ts
226 | const FormData = z
227 | .object({
228 | email: z.string().email(),
229 | password: z.string().min(10),
230 | confirm: z.string().min(10),
231 | })
232 | .refine(obj => obj.password === obj.confirm, {
233 | message: 'Passwords do not match',
234 | path: ['confirm'], // sets the path of the error thrown by this refinement
235 | });
236 | }
237 | ```
238 |
239 | Now lets pass in some invalid data:
240 |
241 | ```ts
242 | FormData.parse({
243 | email: 'not an email',
244 | password: 'tooshort',
245 | confirm: 'nomatch',
246 | });
247 | ```
248 |
249 | This will throw a ZodError with four suberrors:
250 |
251 | ```ts
252 | console.log(err.errors);
253 | /*
254 | [
255 | { code: 'invalid_string', validation: 'email', path: ['email'], message: 'Invalid email' },
256 | {
257 | code: 'too_small',
258 | minimum: 10,
259 | type: 'string',
260 | inclusive: true,
261 | path: ['password'],
262 | message: 'Should be at least 10 characters',
263 | },
264 | {
265 | code: 'too_small',
266 | minimum: 10,
267 | type: 'string',
268 | inclusive: true,
269 | path: ['confirm'],
270 | message: 'Should be at least 10 characters',
271 | },
272 | { code: 'custom_error', message: 'Passwords do not match', path: ['confirm'] },
273 | ];
274 | */
275 | ```
276 |
277 | But using the `formErrors` property, we can "flatten" all those errors down to a form that's much easier to work with:
278 |
279 | ```ts
280 | console.log(err.formErrors);
281 | /*
282 | {
283 | formErrors: [],
284 | fieldErrors: {
285 | email: ['Invalid email'],
286 | password: ['Should be at least 10 characters'],
287 | confirm: ['Should be at least 10 characters', 'Passwords do not match'],
288 | },
289 | }
290 | ```
291 |
292 | - `fieldErrors` is an object. The keys are the field(s) that threw the error. The values are an array of error strings that can be easily presented in the interface.
293 | - `formErrors: string[]` is an array of errors that occured on the root of the form schema. For instance if you called `FormData.parse(null)`, `formErrors` would be:
294 | ```ts
295 | ['Invalid input: expected object, received null'];
296 | ```
297 |
--------------------------------------------------------------------------------
/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: vriad
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Colin McDonnell
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |