mirror of https://github.com/cupcakearmy/zod.git
Cleanup up custom error handling. Wrote ERROR_HANDLING.md.
This commit is contained in:
parent
a4a79728ea
commit
3fe301dd22
|
@ -0,0 +1,211 @@
|
|||
# Error Handling in Zod
|
||||
|
||||
This guide explains Zod's internal error handling system, and the various ways you can customize it for your purposes.
|
||||
|
||||
## ZodError
|
||||
|
||||
All validation errors thrown by Zod are instances of `ZodError`. ZodError is a subclass of `Error`; if you want to place around with this class, you can create an instance like so:
|
||||
|
||||
```ts
|
||||
import * as z from 'zod';
|
||||
|
||||
const myError = new z.ZodError([]);
|
||||
```
|
||||
|
||||
All parsing/validation errors thrown by Zod are instances of `ZodError`. Detailed information about the validation issues is available in the `errors` property.
|
||||
|
||||
## A demonstrative example
|
||||
|
||||
This array represents all errors Zod encounters when attempting to parse a value.
|
||||
|
||||
```ts
|
||||
const person = z.object({
|
||||
names: z.array(z.string()).nonempty(), // at least 1 name
|
||||
address: z.object({
|
||||
line1: z.string(),
|
||||
zipCode: z.number().min(10000), // American 5-digit code
|
||||
}),
|
||||
});
|
||||
|
||||
try {
|
||||
person.parse({
|
||||
names: ['Dave', 12],
|
||||
address: {
|
||||
line1: '123 Maple Ave',
|
||||
zipCode: 123,
|
||||
extra: 'other stuff',
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof z.ZodError) {
|
||||
// ZodSuberror[]
|
||||
console.log(err.errors);
|
||||
} else {
|
||||
// this should never happen
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here are the errors that will be thrown:
|
||||
|
||||
```ts
|
||||
// ZodSuberror[]
|
||||
[
|
||||
{
|
||||
code: 'invalid_type',
|
||||
expected: 'string',
|
||||
received: 'number',
|
||||
path: ['names', 1],
|
||||
message: 'Invalid input: expected string, received number',
|
||||
},
|
||||
{
|
||||
code: 'unrecognized_keys',
|
||||
keys: ['extra'],
|
||||
path: ['address'],
|
||||
message: "Unrecognized key(s) in object: 'extra'",
|
||||
},
|
||||
{
|
||||
code: 'too_small',
|
||||
minimum: 10000,
|
||||
type: 'number',
|
||||
inclusive: true,
|
||||
path: ['address', 'zipCode'],
|
||||
message: 'Value should be greater than or equal to 10000',
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
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 teh input.
|
||||
|
||||
### ZodSuberror
|
||||
|
||||
`ZodSuberror` is _not_ a class. It is a [discriminated union](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions).
|
||||
|
||||
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.
|
||||
|
||||
Every ZodSuberror has these fields:
|
||||
|
||||
| field | type | details |
|
||||
| --------- | --------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `code` | `z.ZodErrorCode` | You can access all possible values using the `z.ZodErrorCode` enum e.g. `z.ZodErrorCode.invalid_type` |
|
||||
| `path` | `(string | number)[]` | `['addresses', 0, 'line1']` |
|
||||
| `message` | `string` | `Invalid type. Expected string, received number.` |
|
||||
|
||||
**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:
|
||||
|
||||
### ZodSuberror
|
||||
|
||||
| code | additional fields |
|
||||
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| ZodErrorCode.invalid_type | `expected: ZodParsedType` <br> `received: ZodParsedType` <br><br>Jump to [this section](#parsedtype) for a breakdownthe possible values of ZodParsedType. |
|
||||
| ZodErrorCode.nonempty_array_is_empty | _no additional properties_ |
|
||||
| ZodErrorCode.unrecognized_keys | `keys: string[]`<br>The list of unrecognized keys<br> |
|
||||
| ZodErrorCode.invalid_union | `unionErrors: ZodError[]` <br> The errors thrown by each elements of the union. |
|
||||
| <!-- | ZodErrorCode.invalid_tuple_length | `expected: number` <br>The expected length.<br> <br>`received: number`<br> The actual length.<br> | --> |
|
||||
| ZodErrorCode.invalid_literal_value | `expected: string | number | boolean` <br> The literal value. |
|
||||
| ZodErrorCode.invalid_enum_value | `options: string[]` <br> The set of acceptable string values for this enum. |
|
||||
| ZodErrorCode.invalid_arguments | `argumentsError: ZodError` <br> 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. |
|
||||
| ZodErrorCode.invalid_return_type | `returnTypeError: ZodError` <br> 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. |
|
||||
| ZodErrorCode.invalid_date | _no additional properties_ |
|
||||
| ZodErrorCode.invalid_string | `validation: "url" | "email" | "uuid"`<br> Which built-in string validator failed |
|
||||
| ZodErrorCode.too_small | `type: "string" | "number" | "array"` <br>The type of the data failing validation<br><br> `minimum: number` <br>The expected length/value.<br><br>`inclusive: boolean`<br>Whether the minimum is included in the range of acceptable values.<br> |
|
||||
| ZodErrorCode.too_big | `type: "string" | "number" | "array"` <br>The type of the data failing validation<br><br> `maximum: number` <br>The expected length/value.<br><br>`inclusive: boolean`<br>Whether the minimum is included in the range of acceptable values.<br> |
|
||||
| ZodErrorCode.custom_error | `params: { [k: string]: any }` <br> 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) |
|
||||
|
||||
### ZodParsedType
|
||||
|
||||
This is an enum used byn Zod internally to represent the type of a parsed value. The possible values are:
|
||||
|
||||
- `string`
|
||||
- `nan`
|
||||
- `number`
|
||||
- `integer`
|
||||
- `boolean`
|
||||
- `date`
|
||||
- `bigint`
|
||||
- `symbol`
|
||||
- `function`
|
||||
- `undefined`
|
||||
- `null`
|
||||
- `array`
|
||||
- `object`
|
||||
- `unknown`
|
||||
- `promise`
|
||||
- `void`
|
||||
|
||||
### Customizing errors with ZodErrorMap
|
||||
|
||||
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.
|
||||
|
||||
`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.
|
||||
|
||||
- `error: Omit<ZodSuberror, "message">`
|
||||
|
||||
As mentioned above, ZodSuberror is a discriminated union.
|
||||
|
||||
- `ctx: { defaultError: string; data: any}`
|
||||
|
||||
- `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
|
||||
|
||||
- `ctx.data` contains the data that was passed into `.parse`. You can use this to customize the error message.
|
||||
|
||||
### A working example
|
||||
|
||||
Let's look at a practical example of of customized error map:
|
||||
|
||||
```ts
|
||||
import * as z from 'zod';
|
||||
|
||||
const errorMap: z.ZodErrorMap = (error, ctx) => {
|
||||
/*
|
||||
|
||||
If error.message is set, that means the user is trying to
|
||||
override the error message. This is how method-specific
|
||||
error overrides work, like this:
|
||||
|
||||
z.string().min(5, { message: "TOO SMALL 🤬" })
|
||||
|
||||
It is a best practice to return `error.message` if it is set.
|
||||
|
||||
*/
|
||||
if (error.message) return { message: error.message };
|
||||
|
||||
/*
|
||||
This is where you override the various error codes
|
||||
*/
|
||||
switch (error.code) {
|
||||
case z.ZodErrorCode.invalid_type:
|
||||
if (error.expected === 'string') {
|
||||
return { message: `This ain't a string!` };
|
||||
}
|
||||
break;
|
||||
case z.ZodErrorCode.custom_error:
|
||||
// produce a custom message using error.params
|
||||
// error.params won't be set unless you passed
|
||||
// a `params` arguments into a custom validator
|
||||
const params = error.params || {};
|
||||
if (params.myField) {
|
||||
return { message: `Bad input: ${params.myField}` };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// fall back to default message!
|
||||
return { message: ctx.defaultError };
|
||||
};
|
||||
|
||||
z.string().parse(12, { errorMap });
|
||||
|
||||
/* throws:
|
||||
ZodError {
|
||||
errors: [{
|
||||
code: "invalid_type",
|
||||
path: [],
|
||||
message: "This ain't a string!",
|
||||
expected: "string",
|
||||
received: "number",
|
||||
}]
|
||||
}
|
||||
*/
|
||||
```
|
|
@ -0,0 +1 @@
|
|||
github: vriad
|
103
README.md
103
README.md
|
@ -73,15 +73,24 @@ npm install --save zod
|
|||
yarn add zod
|
||||
```
|
||||
|
||||
#### TypeScript versions
|
||||
#### TypeScript requirements
|
||||
|
||||
⚠ Zod 2.0.x requires TypeScript 4.0+
|
||||
|
||||
> You must use strict mode for Zod to correctly infer the types of your schemas! Add `"strict": true` inside "compilerOptions" in your `tsconfig.json`.
|
||||
1. Zod 1.x requires TypeScript 3.2+
|
||||
2. You must configure your project to use TypeScript's **strict mode**. Otherwise Zod can't correctly infer the types of your schemas!
|
||||
```ts
|
||||
// tsconfig.json
|
||||
{
|
||||
// ...
|
||||
"compilerOptions": {
|
||||
// ...
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
Zod is a validation library designed for optimal developer experience. It's a TypeScript-first schema declaration library with rigorous (and correct!) inferred types, incredible developer experience, and a few killer features missing from the existing libraries.
|
||||
Zod is a validation library designed for optimal developer experience. It's a TypeScript-first schema declaration library with rigorous inferred types, incredible developer experience, and a few killer features missing from the existing libraries.
|
||||
|
||||
<!-- - It infers the statically types of your schemas
|
||||
- Eliminates the need to keep static types and runtime validators in sync by hand
|
||||
|
@ -112,6 +121,7 @@ z.date();
|
|||
// empty types
|
||||
z.undefined();
|
||||
z.null();
|
||||
z.void();
|
||||
|
||||
// catch-all types
|
||||
z.any();
|
||||
|
@ -223,18 +233,20 @@ z.string().max(5);
|
|||
z.string().length(5);
|
||||
z.string().email();
|
||||
z.string().url();
|
||||
z.string().uuid();
|
||||
```
|
||||
|
||||
### Custom error messages
|
||||
|
||||
Like `.refine`, the final argument accepts a custom error message.
|
||||
Like `.refine`, The final (optional) argument is an object that lets you provide a custom error in the `message` field.
|
||||
|
||||
```ts
|
||||
z.string().min(5, 'Must be 5 or more characters long');
|
||||
z.string().max(5, 'Must be 5 or fewer characters long');
|
||||
z.string().length(5, 'Must be exactly 5 characters long');
|
||||
z.string().email('Invalid email address.');
|
||||
z.string().url('Invalid url');
|
||||
z.string().min(5, { message: 'Must be 5 or more characters long' });
|
||||
z.string().max(5, { message: 'Must be 5 or fewer characters long' });
|
||||
z.string().length(5, { message: 'Must be exactly 5 characters long' });
|
||||
z.string().email({ message: 'Invalid email address.' });
|
||||
z.string().url({ message: 'Invalid url' });
|
||||
z.string().url({ message: 'Invalid url' });
|
||||
```
|
||||
|
||||
> To see the email and url regexes, check out [this file](https://github.com/vriad/zod/blob/master/src/types/string.ts). To use a more advanced method, use a custom refinement.
|
||||
|
@ -243,11 +255,11 @@ z.string().url('Invalid url');
|
|||
|
||||
There are a handful of number-specific validations.
|
||||
|
||||
All of these validations allow you to _optionally_ specify a custom error message as a final `string` argument.
|
||||
The final (optional) argument is a params object that lets you provide a custom error in the `message` field.
|
||||
|
||||
```ts
|
||||
z.number().min(5);
|
||||
z.number().max(5);
|
||||
z.number().max(5, { message: 'this👏is👏too👏big' });
|
||||
|
||||
z.number().int(); // value must be an integer
|
||||
|
||||
|
@ -928,6 +940,8 @@ You can create a function schema with `z.function(args, returnType)` which accep
|
|||
- `args: ZodTuple` The first argument is a tuple (created with `z.tuple([...])` and defines the schema of the arguments to your function. If the function doesn't accept arguments, you can pass an empty tuple (`z.tuple([])`).
|
||||
- `returnType: any Zod schema` The second argument is the function's return type. This can be any Zod schema.
|
||||
|
||||
> You can the special `z.void()` option if your function doesn't return anything. This will let Zod properly infer the type of void-returning functions. (Void-returning function can actually return either undefined or null.)
|
||||
|
||||
```ts
|
||||
const args = z.tuple([z.string()]);
|
||||
|
||||
|
@ -1068,68 +1082,7 @@ User.omit({ outer: { inner: { prop2: true } } }); // { outer: { prop1: string, i
|
|||
|
||||
## Errors
|
||||
|
||||
Zod includes a custom `Error` subclass called `ZodError`. All validation errors thrown by Zod are instances of `ZodError`.
|
||||
|
||||
A `ZodError` instance has an `errors` property of type
|
||||
|
||||
```ts
|
||||
// ZodError#errors
|
||||
{
|
||||
path: (string | number)[],
|
||||
message: string
|
||||
}[]
|
||||
```
|
||||
|
||||
This array represents all errors Zod encounters when attempting to parse a value.
|
||||
|
||||
```ts
|
||||
const person = z.object({
|
||||
name: {
|
||||
first: z.string(),
|
||||
last: z.string(),
|
||||
},
|
||||
age: z.number(),
|
||||
address: z.array(z.string()),
|
||||
});
|
||||
|
||||
try {
|
||||
person.parse({
|
||||
name: { first: 'Dave', last: 42 },
|
||||
age: 'threeve',
|
||||
address: ['123 Maple Street', {}],
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof ZodError) {
|
||||
console.log(JSON.stringify(err.errors));
|
||||
/*
|
||||
[
|
||||
{
|
||||
"path": [ "name", "last" ],
|
||||
"message": "Non-string type: number"
|
||||
},
|
||||
{
|
||||
"path": [ "age" ],
|
||||
"message": "Non-number type: string"
|
||||
},
|
||||
{
|
||||
"path": [ "address", 1 ],
|
||||
"message": "Non-string type: object"
|
||||
}
|
||||
]
|
||||
*/
|
||||
|
||||
// err.message returns a formatted error message
|
||||
console.log(err.message);
|
||||
/*
|
||||
`name.last`: Non-string type: number
|
||||
`age`: Non-number type: string
|
||||
`address.1`: Non-string type: object
|
||||
*/
|
||||
} else {
|
||||
// should never happen
|
||||
}
|
||||
}
|
||||
```
|
||||
There is a dedicated guide on Zod's error handling system here: [ERROR_HANDLING.md](https://github.com/vriad/zod/blob/master/ERROR_HANDLING.md)
|
||||
|
||||
# Comparison
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="116" height="20"><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#dfb317"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" textLength="530">Coverage</text><text x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">89.71%</text><text x="885" y="140" transform="scale(.1)" textLength="430">89.71%</text></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="116" height="20"><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#dfb317"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" textLength="530">Coverage</text><text x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">89.63%</text><text x="885" y="140" transform="scale(.1)" textLength="430">89.63%</text></g></svg>
|
Before Width: | Height: | Size: 1015 B After Width: | Height: | Size: 1015 B |
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "zod",
|
||||
"version": "2.0.0-alpha.7",
|
||||
"version": "1.8.0-beta.1",
|
||||
"description": "TypeScript-first schema declaration and validation library with static type inference",
|
||||
"main": "./lib/src/index.js",
|
||||
"types": "./lib/src/index.d.ts",
|
||||
|
@ -53,6 +53,6 @@
|
|||
"ts-jest": "^25.2.1",
|
||||
"tslint": "^6.1.0",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"typescript": "3.9"
|
||||
"typescript": "3.2"
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { ParsedType } from './parser';
|
||||
import { ZodParsedType } from './parser';
|
||||
import { util } from './helpers/util';
|
||||
|
||||
export const ZodErrorCode = util.arrayToEnum([
|
||||
|
@ -6,14 +6,20 @@ export const ZodErrorCode = util.arrayToEnum([
|
|||
'nonempty_array_is_empty',
|
||||
'custom_error',
|
||||
'invalid_union',
|
||||
'invalid_array_length',
|
||||
'array_empty',
|
||||
// 'invalid_tuple_length',
|
||||
'invalid_literal_value',
|
||||
'invalid_enum_value',
|
||||
'unrecognized_keys',
|
||||
'invalid_arguments',
|
||||
'invalid_return_type',
|
||||
'invalid_date',
|
||||
// 'too_short',
|
||||
// 'too_long',
|
||||
'invalid_string',
|
||||
// 'invalid_url',
|
||||
// 'invalid_uuid',
|
||||
'too_small',
|
||||
'too_big',
|
||||
]);
|
||||
|
||||
export type ZodErrorCode = keyof typeof ZodErrorCode;
|
||||
|
@ -22,13 +28,12 @@ export type ZodSuberrorBase = {
|
|||
path: (string | number)[];
|
||||
code: ZodErrorCode;
|
||||
message?: string;
|
||||
suberrors?: ZodError[];
|
||||
};
|
||||
|
||||
interface InvalidTypeError extends ZodSuberrorBase {
|
||||
code: typeof ZodErrorCode.invalid_type;
|
||||
expected: ParsedType;
|
||||
received: ParsedType;
|
||||
expected: ZodParsedType;
|
||||
received: ZodParsedType;
|
||||
}
|
||||
|
||||
interface NonEmptyArrayIsEmptyError extends ZodSuberrorBase {
|
||||
|
@ -42,13 +47,14 @@ interface UnrecognizedKeysError extends ZodSuberrorBase {
|
|||
|
||||
interface InvalidUnionError extends ZodSuberrorBase {
|
||||
code: typeof ZodErrorCode.invalid_union;
|
||||
unionErrors: ZodError[];
|
||||
}
|
||||
|
||||
interface InvalidArrayLengthError extends ZodSuberrorBase {
|
||||
code: typeof ZodErrorCode.invalid_array_length;
|
||||
expected: number;
|
||||
received: number;
|
||||
}
|
||||
// interface InvalidArrayLengthError extends ZodSuberrorBase {
|
||||
// code: typeof ZodErrorCode.invalid_tuple_length;
|
||||
// expected: number;
|
||||
// received: number;
|
||||
// }
|
||||
|
||||
interface InvalidLiteralValueError extends ZodSuberrorBase {
|
||||
code: typeof ZodErrorCode.invalid_literal_value;
|
||||
|
@ -62,16 +68,57 @@ interface InvalidEnumValueError extends ZodSuberrorBase {
|
|||
|
||||
interface InvalidArgumentsError extends ZodSuberrorBase {
|
||||
code: typeof ZodErrorCode.invalid_arguments;
|
||||
argumentsError: ZodError;
|
||||
}
|
||||
|
||||
interface InvalidReturnTypeError extends ZodSuberrorBase {
|
||||
code: typeof ZodErrorCode.invalid_return_type;
|
||||
returnTypeError: ZodError;
|
||||
}
|
||||
|
||||
interface InvalidDateError extends ZodSuberrorBase {
|
||||
code: typeof ZodErrorCode.invalid_date;
|
||||
}
|
||||
|
||||
// interface TooShortError extends ZodSuberrorBase {
|
||||
// code: typeof ZodErrorCode.too_small;
|
||||
// minimum: number;
|
||||
// }
|
||||
|
||||
// interface TooLongError extends ZodSuberrorBase {
|
||||
// code: typeof ZodErrorCode.too_big;
|
||||
// maximum: number;
|
||||
// }
|
||||
|
||||
interface InvalidStringError extends ZodSuberrorBase {
|
||||
code: typeof ZodErrorCode.invalid_string;
|
||||
validation: 'email' | 'url' | 'uuid';
|
||||
}
|
||||
|
||||
// interface InvalidUrlError extends ZodSuberrorBase {
|
||||
// code: typeof ZodErrorCode.invalid_url;
|
||||
// validation: | 'url';
|
||||
// }
|
||||
|
||||
// interface InvalidUuidError extends ZodSuberrorBase {
|
||||
// code: typeof ZodErrorCode.invalid_uuid;
|
||||
// validation: | 'uuid';
|
||||
// }
|
||||
|
||||
interface TooSmallError extends ZodSuberrorBase {
|
||||
code: typeof ZodErrorCode.too_small;
|
||||
minimum: number;
|
||||
inclusive: boolean;
|
||||
type: 'array' | 'string' | 'number';
|
||||
}
|
||||
|
||||
interface TooBigError extends ZodSuberrorBase {
|
||||
code: typeof ZodErrorCode.too_big;
|
||||
maximum: number;
|
||||
inclusive: boolean;
|
||||
type: 'array' | 'string' | 'number';
|
||||
}
|
||||
|
||||
interface CustomError extends ZodSuberrorBase {
|
||||
code: typeof ZodErrorCode.custom_error;
|
||||
params?: { [k: string]: any };
|
||||
|
@ -82,12 +129,19 @@ export type ZodSuberrorOptionalMessage =
|
|||
| NonEmptyArrayIsEmptyError
|
||||
| UnrecognizedKeysError
|
||||
| InvalidUnionError
|
||||
| InvalidArrayLengthError
|
||||
// | InvalidArrayLengthError
|
||||
| InvalidLiteralValueError
|
||||
| InvalidEnumValueError
|
||||
| InvalidArgumentsError
|
||||
| InvalidReturnTypeError
|
||||
| InvalidDateError
|
||||
// | TooShortError
|
||||
// | TooLongError
|
||||
| InvalidStringError // | InvalidEmailError
|
||||
// | InvalidUrlError
|
||||
// | InvalidUuidError
|
||||
| TooSmallError
|
||||
| TooBigError
|
||||
| CustomError;
|
||||
|
||||
export type ZodSuberror = ZodSuberrorOptionalMessage & { message: string };
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import * as z from '..';
|
||||
import { ZodError, ZodErrorCode } from '../ZodError';
|
||||
import { ParsedType } from '../parser';
|
||||
import { ZodParsedType } from '../parser';
|
||||
|
||||
test('error creation', () => {
|
||||
const err1 = ZodError.create([]);
|
||||
err1.addError({
|
||||
code: ZodErrorCode.invalid_type,
|
||||
expected: ParsedType.object,
|
||||
received: ParsedType.string,
|
||||
expected: ZodParsedType.object,
|
||||
received: ZodParsedType.string,
|
||||
path: [],
|
||||
message: '',
|
||||
});
|
||||
|
@ -22,25 +22,49 @@ test('error creation', () => {
|
|||
err3.message;
|
||||
});
|
||||
|
||||
test('custom errormap', () => {
|
||||
const errorMap: z.ZodErrorMap = (error, ctx) => {
|
||||
if (error.code === ZodErrorCode.invalid_type) {
|
||||
if (error.expected === 'string') {
|
||||
return "This ain't no string!";
|
||||
}
|
||||
const errorMap: z.ZodErrorMap = (error, ctx) => {
|
||||
if (error.code === ZodErrorCode.invalid_type) {
|
||||
if (error.expected === 'string') {
|
||||
return { message: 'bad type!' };
|
||||
}
|
||||
if (error.code === ZodErrorCode.custom_error) {
|
||||
return JSON.stringify(error.params, null, 2);
|
||||
}
|
||||
return ctx.defaultError;
|
||||
};
|
||||
errorMap;
|
||||
}
|
||||
if (error.code === ZodErrorCode.custom_error) {
|
||||
return { message: `less-than-${(error.params || {}).minimum}` };
|
||||
}
|
||||
return { message: ctx.defaultError };
|
||||
};
|
||||
|
||||
test('type error with custom error map', () => {
|
||||
try {
|
||||
z.string().parse('asdf', { errorMap });
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err;
|
||||
expect(zerr.errors[0].code).toEqual(z.ZodErrorCode.invalid_type);
|
||||
expect(zerr.errors[0].message).toEqual(`bad type!`);
|
||||
}
|
||||
});
|
||||
|
||||
test('refinement fail with params', () => {
|
||||
try {
|
||||
z.number()
|
||||
.refinement({
|
||||
check: val => val >= 3,
|
||||
params: { minimum: 3 },
|
||||
})
|
||||
.parse(2, { errorMap });
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err;
|
||||
expect(zerr.errors[0].code).toEqual(z.ZodErrorCode.custom_error);
|
||||
expect(zerr.errors[0].message).toEqual(`less-than-3`);
|
||||
}
|
||||
});
|
||||
|
||||
test('custom error with custom errormap', () => {
|
||||
try {
|
||||
z.string()
|
||||
.refinement({
|
||||
check: val => val.length > 12,
|
||||
// params: { test: 15 },
|
||||
params: { minimum: 13 },
|
||||
message: 'override',
|
||||
})
|
||||
.parse('asdf', { errorMap });
|
||||
|
@ -50,4 +74,69 @@ test('custom errormap', () => {
|
|||
}
|
||||
});
|
||||
|
||||
test('default error message', () => {
|
||||
try {
|
||||
z.number()
|
||||
.refine(x => x > 3)
|
||||
.parse(2);
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err;
|
||||
expect(zerr.errors.length).toEqual(1);
|
||||
expect(zerr.errors[0].message).toEqual('Invalid value.');
|
||||
}
|
||||
});
|
||||
|
||||
test('override error in refine', () => {
|
||||
try {
|
||||
z.number()
|
||||
.refine(x => x > 3, 'override')
|
||||
.parse(2);
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err;
|
||||
expect(zerr.errors.length).toEqual(1);
|
||||
expect(zerr.errors[0].message).toEqual('override');
|
||||
}
|
||||
});
|
||||
|
||||
test('override error in refinement', () => {
|
||||
try {
|
||||
z.number()
|
||||
.refinement({
|
||||
check: x => x > 3,
|
||||
message: 'override',
|
||||
})
|
||||
.parse(2);
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err;
|
||||
expect(zerr.errors.length).toEqual(1);
|
||||
expect(zerr.errors[0].message).toEqual('override');
|
||||
}
|
||||
});
|
||||
|
||||
test('array minimum', () => {
|
||||
try {
|
||||
z.array(z.string())
|
||||
.min(3, 'tooshort')
|
||||
.parse(['asdf', 'qwer']);
|
||||
} catch (err) {
|
||||
const zerr: ZodError = err;
|
||||
expect(zerr.errors[0].code).toEqual(ZodErrorCode.too_small);
|
||||
expect(zerr.errors[0].message).toEqual('tooshort');
|
||||
}
|
||||
try {
|
||||
z.array(z.string())
|
||||
.min(3)
|
||||
.parse(['asdf', 'qwer']);
|
||||
} catch (err) {
|
||||
const zerr: ZodError = err;
|
||||
expect(zerr.errors[0].code).toEqual(ZodErrorCode.too_small);
|
||||
expect(zerr.errors[0].message).toEqual(`Should have at least 3 items`);
|
||||
}
|
||||
});
|
||||
|
||||
// implement test for semi-smart union logic that checks for type error on either left or right
|
||||
test('union smart errors', () => {
|
||||
try {
|
||||
z.union([z.string(), z.number().int()]).parse(3.2);
|
||||
} catch (err) {}
|
||||
});
|
||||
|
|
|
@ -116,15 +116,18 @@ test('special function error codes', () => {
|
|||
checker('12' as any);
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err;
|
||||
expect(zerr.errors[0].code).toEqual(z.ZodErrorCode.invalid_return_type);
|
||||
expect(zerr.errors[0].suberrors!.length).toEqual(1);
|
||||
const first = zerr.errors[0];
|
||||
if (first.code !== z.ZodErrorCode.invalid_return_type) throw new Error();
|
||||
|
||||
expect(first.returnTypeError).toBeInstanceOf(z.ZodError);
|
||||
}
|
||||
|
||||
try {
|
||||
checker(12 as any);
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err;
|
||||
expect(zerr.errors[0].code).toEqual(z.ZodErrorCode.invalid_arguments);
|
||||
expect(zerr.errors[0].suberrors!.length).toEqual(1);
|
||||
const first = zerr.errors[0];
|
||||
if (first.code !== z.ZodErrorCode.invalid_arguments) throw new Error();
|
||||
expect(first.argumentsError).toBeInstanceOf(z.ZodError);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import * as z from '..';
|
||||
|
||||
test('array min', () => {
|
||||
z.array(z.string())
|
||||
.min(4)
|
||||
.parseAsync([])
|
||||
.catch(err => {
|
||||
expect(err.errors[0].message).toEqual('Should have at least 4 items');
|
||||
});
|
||||
});
|
||||
|
||||
test('array max', () => {
|
||||
z.array(z.string())
|
||||
.max(2)
|
||||
.parseAsync(['asdf', 'asdf', 'asdf'])
|
||||
.catch(err => {
|
||||
expect(err.errors[0].message).toEqual('Should have at most 2 items');
|
||||
});
|
||||
});
|
||||
|
||||
test('string min', () => {
|
||||
z.string()
|
||||
.min(4)
|
||||
.parseAsync('asd')
|
||||
.catch(err => {
|
||||
expect(err.errors[0].message).toEqual('Should be at least 4 characters');
|
||||
});
|
||||
});
|
||||
|
||||
test('string max', () => {
|
||||
z.string()
|
||||
.max(4)
|
||||
.parseAsync('aasdfsdfsd')
|
||||
.catch(err => {
|
||||
expect(err.errors[0].message).toEqual('Should be at most 4 characters long');
|
||||
});
|
||||
});
|
||||
|
||||
test('number min', () => {
|
||||
z.number()
|
||||
.min(3)
|
||||
.parseAsync(2)
|
||||
.catch(err => {
|
||||
expect(err.errors[0].message).toEqual('Value should be greater than or equal to 3');
|
||||
});
|
||||
});
|
||||
|
||||
test('number max', () => {
|
||||
z.number()
|
||||
.max(3)
|
||||
.parseAsync(4)
|
||||
.catch(err => {
|
||||
expect(err.errors[0].message).toEqual('Value should be less than or equal to 3');
|
||||
});
|
||||
});
|
||||
|
||||
test('number nonnegative', () => {
|
||||
z.number()
|
||||
.nonnegative()
|
||||
.parseAsync(-1)
|
||||
.catch(err => {
|
||||
expect(err.errors[0].message).toEqual('Value should be greater than or equal to 0');
|
||||
});
|
||||
});
|
||||
|
||||
test('number nonpositive', () => {
|
||||
z.number()
|
||||
.nonpositive()
|
||||
.parseAsync(1)
|
||||
.catch(err => {
|
||||
expect(err.errors[0].message).toEqual('Value should be less than or equal to 0');
|
||||
});
|
||||
});
|
||||
|
||||
test('number negative', () => {
|
||||
z.number()
|
||||
.negative()
|
||||
.parseAsync(1)
|
||||
.catch(err => {
|
||||
expect(err.errors[0].message).toEqual('Value should be less than 0');
|
||||
});
|
||||
});
|
||||
|
||||
test('number positive', () => {
|
||||
z.number()
|
||||
.positive()
|
||||
.parseAsync(-1)
|
||||
.catch(err => {
|
||||
expect(err.errors[0].message).toEqual('Value should be greater than 0');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,90 @@
|
|||
import { ZodErrorCode, ZodSuberrorOptionalMessage } from './ZodError';
|
||||
import { util } from './helpers/util';
|
||||
|
||||
type ErrorMapCtx = {
|
||||
// path: (string | number)[];
|
||||
// details: any;
|
||||
defaultError: string;
|
||||
data: any;
|
||||
// metadata: object;
|
||||
};
|
||||
|
||||
export type ZodErrorMap = typeof defaultErrorMap;
|
||||
export const defaultErrorMap = (error: ZodSuberrorOptionalMessage, _ctx: ErrorMapCtx): { message: string } => {
|
||||
let message: string;
|
||||
switch (error.code) {
|
||||
case ZodErrorCode.invalid_type:
|
||||
message = `Invalid input: expected ${error.expected}, received ${error.received}`;
|
||||
break;
|
||||
case ZodErrorCode.nonempty_array_is_empty:
|
||||
message = `List must contain at least one item`;
|
||||
break;
|
||||
case ZodErrorCode.unrecognized_keys:
|
||||
message = `Unrecognized key(s) in object: ${error.keys.map(k => `'${k}'`).join(', ')}`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_union:
|
||||
message = `Invalid input`;
|
||||
break;
|
||||
// case ZodErrorCode.invalid_tuple_length:
|
||||
// message = `Expected list of ${error.expected} items, received ${error.received} items`;
|
||||
// break;
|
||||
case ZodErrorCode.invalid_literal_value:
|
||||
message = `Input must be "${error.expected}"`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_enum_value:
|
||||
message = `Input must be one of these values: ${error.options.join(', ')}`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_arguments:
|
||||
message = `Invalid function arguments`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_return_type:
|
||||
message = `Invalid function return type`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_date:
|
||||
message = `Invalid date`;
|
||||
break;
|
||||
// case ZodErrorCode.too_small:
|
||||
// const tooShortNoun = _ctx.data === 'string' ? 'characters' : 'items';
|
||||
// message = `Too short, should be at least ${error.minimum} ${tooShortNoun}`;
|
||||
// break;
|
||||
// case ZodErrorCode.too_big:
|
||||
// const tooLongNoun = _ctx.data === 'string' ? 'characters' : 'items';
|
||||
// message = `Too short, should be at most ${error.maximum} ${tooLongNoun}`;
|
||||
// break;
|
||||
case ZodErrorCode.invalid_string:
|
||||
message = `Invalid ${error.validation}`;
|
||||
break;
|
||||
// case ZodErrorCode.invalid_url:
|
||||
// message = 'Invalid URL.';
|
||||
// break;
|
||||
// case ZodErrorCode.invalid_uuid:
|
||||
// message = 'Invalid UUID.';
|
||||
// break;
|
||||
case ZodErrorCode.too_small:
|
||||
if (error.type === 'array')
|
||||
message = `Should have ${error.inclusive ? `at least` : `more than`} ${error.minimum} items`;
|
||||
else if (error.type === 'string')
|
||||
message = `Should be ${error.inclusive ? `at least` : `over`} ${error.minimum} characters`;
|
||||
else if (error.type === 'number')
|
||||
message = `Value should be greater than ${error.inclusive ? `or equal to ` : ``}${error.minimum}`;
|
||||
else message = 'Invalid input';
|
||||
break;
|
||||
case ZodErrorCode.too_big:
|
||||
if (error.type === 'array')
|
||||
message = `Should have ${error.inclusive ? `at most` : `less than`} ${error.maximum} items`;
|
||||
else if (error.type === 'string')
|
||||
message = `Should be ${error.inclusive ? `at most` : `under`} ${error.maximum} characters long`;
|
||||
else if (error.type === 'number')
|
||||
message = `Value should be less than ${error.inclusive ? `or equal to ` : ``}${error.maximum}`;
|
||||
else message = 'Invalid input';
|
||||
break;
|
||||
case ZodErrorCode.custom_error:
|
||||
message = `Invalid input.`;
|
||||
break;
|
||||
default:
|
||||
message = `Invalid input.`;
|
||||
util.assertNever(error);
|
||||
}
|
||||
return { message };
|
||||
// return `Invalid input.`;
|
||||
};
|
|
@ -1,55 +0,0 @@
|
|||
import { ZodErrorCode, ZodSuberrorOptionalMessage } from './ZodError';
|
||||
import { util } from './helpers/util';
|
||||
|
||||
type ErrorMapCtx = {
|
||||
// path: (string | number)[];
|
||||
// details: any;
|
||||
defaultError: string;
|
||||
data: any;
|
||||
metadata: object;
|
||||
};
|
||||
|
||||
export type ZodErrorMap = typeof defaultErrorMap;
|
||||
export const defaultErrorMap = (error: ZodSuberrorOptionalMessage, _ctx: ErrorMapCtx): string => {
|
||||
let message: string;
|
||||
switch (error.code) {
|
||||
case ZodErrorCode.invalid_type:
|
||||
message = `Invalid input: expected ${error.expected}, received ${error.received}`;
|
||||
break;
|
||||
case ZodErrorCode.nonempty_array_is_empty:
|
||||
message = `List must contain at least one item`;
|
||||
break;
|
||||
case ZodErrorCode.unrecognized_keys:
|
||||
message = `Unrecognized key(s) in object: ${error.keys.map(k => `'${k}'`).join(', ')}`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_union:
|
||||
message = `Invalid input`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_array_length:
|
||||
message = `Expected list of ${error.expected} items, received ${error.received} items`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_literal_value:
|
||||
message = `Input must be "${error.expected}"`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_enum_value:
|
||||
message = `Input must be one of these values: ${error.options.join(', ')}`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_arguments:
|
||||
message = `Invalid function arguments`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_return_type:
|
||||
message = `Invalid function return type`;
|
||||
break;
|
||||
case ZodErrorCode.invalid_date:
|
||||
message = `Invalid date`;
|
||||
break;
|
||||
case ZodErrorCode.custom_error:
|
||||
message = `Invalid input.`;
|
||||
break;
|
||||
default:
|
||||
message = `Invalid input.`;
|
||||
util.assertNever(error);
|
||||
}
|
||||
return message;
|
||||
// return `Invalid input.`;
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
export namespace errorUtil {
|
||||
export type ErrMessage = string | { message?: string };
|
||||
export const errToObj = (message?: ErrMessage) => (typeof message === 'string' ? { message } : message || {});
|
||||
}
|
|
@ -23,7 +23,9 @@ import { ZodEnum, ZodEnumDef } from './types/enum';
|
|||
import { ZodPromise, ZodPromiseDef } from './types/promise';
|
||||
import { TypeOf, ZodType, ZodTypeAny, ZodTypeDef, ZodTypes } from './types/base';
|
||||
import { ZodError, ZodErrorCode } from './ZodError';
|
||||
import { ZodErrorMap } from './errorMap';
|
||||
import { ZodParsedType } from './parser';
|
||||
|
||||
import { ZodErrorMap } from './defaultErrorMap';
|
||||
|
||||
import { ZodCodeGenerator } from './codegen';
|
||||
|
||||
|
@ -170,6 +172,7 @@ export {
|
|||
ZodError,
|
||||
ZodErrorMap,
|
||||
ZodErrorCode,
|
||||
ZodParsedType,
|
||||
ZodCodeGenerator,
|
||||
};
|
||||
|
||||
|
|
111
src/parser.ts
111
src/parser.ts
|
@ -2,7 +2,7 @@ import * as z from './types/base';
|
|||
import { ZodDef } from '.';
|
||||
import { ZodError, ZodErrorCode, ZodSuberror, ZodSuberrorOptionalMessage } from './ZodError';
|
||||
import { util } from './helpers/util';
|
||||
import { ZodErrorMap, defaultErrorMap } from './errorMap';
|
||||
import { ZodErrorMap, defaultErrorMap } from './defaultErrorMap';
|
||||
|
||||
export type ParseParams = {
|
||||
seen?: { schema: any; objects: any[] }[];
|
||||
|
@ -10,7 +10,7 @@ export type ParseParams = {
|
|||
errorMap?: ZodErrorMap;
|
||||
};
|
||||
|
||||
export const getParsedType = (data: any): ParsedType => {
|
||||
export const getParsedType = (data: any): ZodParsedType => {
|
||||
if (typeof data === 'string') return 'string';
|
||||
if (typeof data === 'number') {
|
||||
if (Number.isNaN(data)) return 'nan';
|
||||
|
@ -40,10 +40,11 @@ export const getParsedType = (data: any): ParsedType => {
|
|||
return 'unknown';
|
||||
};
|
||||
|
||||
export const ParsedType = util.arrayToEnum([
|
||||
export const ZodParsedType = util.arrayToEnum([
|
||||
'string',
|
||||
'nan',
|
||||
'number',
|
||||
'integer',
|
||||
'boolean',
|
||||
'date',
|
||||
'bigint',
|
||||
|
@ -59,9 +60,10 @@ export const ParsedType = util.arrayToEnum([
|
|||
]);
|
||||
|
||||
// export const ParsedType = arrayToEnum(ParsedTypeArray);
|
||||
export type ParsedType = keyof typeof ParsedType;
|
||||
export type ZodParsedType = keyof typeof ZodParsedType;
|
||||
|
||||
type StripErrorKeys<T extends object> = T extends any ? util.OmitKeys<T, 'path'> : never;
|
||||
export type MakeErrorData = StripErrorKeys<ZodSuberrorOptionalMessage>;
|
||||
|
||||
export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
||||
obj: any,
|
||||
|
@ -84,21 +86,19 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
// },
|
||||
};
|
||||
|
||||
const makeError = (
|
||||
errorData: StripErrorKeys<ZodSuberrorOptionalMessage>,
|
||||
// details: object = {},
|
||||
): ZodSuberror => {
|
||||
const makeError = (errorData: MakeErrorData): ZodSuberror => {
|
||||
const errorArg = { ...errorData, path: params.path };
|
||||
const ctxArg = { metadata: {}, data: obj };
|
||||
const ctxArg = { data: obj };
|
||||
|
||||
const defaultError =
|
||||
defaultErrorMap === params.errorMap
|
||||
? `Invalid value`
|
||||
? { message: `Invalid value.` }
|
||||
: defaultErrorMap(errorArg, { ...ctxArg, defaultError: `Invalid value.` });
|
||||
return {
|
||||
...errorData,
|
||||
path: params.path,
|
||||
message: errorData.message || params.errorMap(errorArg, { ...ctxArg, defaultError }),
|
||||
message:
|
||||
errorData.message || params.errorMap(errorArg, { ...ctxArg, defaultError: defaultError.message }).message,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -141,34 +141,34 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
switch (def.t) {
|
||||
case z.ZodTypes.string:
|
||||
// error.addError()
|
||||
if (parsedType !== ParsedType.string) {
|
||||
if (parsedType !== ZodParsedType.string) {
|
||||
//throw ZodError.fromString(`Non-string type: ${typeof obj}`);
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ParsedType.string, received: parsedType }),
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ZodParsedType.string, received: parsedType }),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
// return obj as any;
|
||||
break;
|
||||
case z.ZodTypes.number:
|
||||
if (parsedType !== ParsedType.number) {
|
||||
if (parsedType !== ZodParsedType.number) {
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ParsedType.number, received: parsedType }),
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ZodParsedType.number, received: parsedType }),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
if (Number.isNaN(obj)) {
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ParsedType.number, received: ParsedType.nan }),
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ZodParsedType.number, received: ZodParsedType.nan }),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
// return obj as any;
|
||||
break;
|
||||
case z.ZodTypes.bigint:
|
||||
if (parsedType !== ParsedType.bigint) {
|
||||
if (parsedType !== ZodParsedType.bigint) {
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ParsedType.number, received: parsedType }),
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ZodParsedType.number, received: parsedType }),
|
||||
);
|
||||
throw error;
|
||||
// throw ZodError.fromString(`Non-bigint type: ${typeof obj}.`);
|
||||
|
@ -176,19 +176,19 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
// return obj;
|
||||
break;
|
||||
case z.ZodTypes.boolean:
|
||||
if (parsedType !== ParsedType.boolean) {
|
||||
if (parsedType !== ZodParsedType.boolean) {
|
||||
// throw ZodError.fromString(`Non-boolean type: ${typeof obj}`);
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ParsedType.boolean, received: parsedType }),
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ZodParsedType.boolean, received: parsedType }),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
// return obj as any;
|
||||
break;
|
||||
case z.ZodTypes.undefined:
|
||||
if (parsedType !== ParsedType.undefined) {
|
||||
if (parsedType !== ZodParsedType.undefined) {
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ParsedType.undefined, received: parsedType }),
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ZodParsedType.undefined, received: parsedType }),
|
||||
);
|
||||
throw error;
|
||||
// throw ZodError.fromString(`Non-undefined type:Found: ${typeof obj}`);
|
||||
|
@ -196,9 +196,11 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
// return undefined;
|
||||
break;
|
||||
case z.ZodTypes.null:
|
||||
if (parsedType !== ParsedType.null) {
|
||||
if (parsedType !== ZodParsedType.null) {
|
||||
// throw ZodError.fromString(`Non-null type: ${typeof obj}`);
|
||||
error.addError(makeError({ code: ZodErrorCode.invalid_type, expected: ParsedType.null, received: parsedType }));
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ZodParsedType.null, received: parsedType }),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
break;
|
||||
|
@ -207,16 +209,18 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
case z.ZodTypes.unknown:
|
||||
break;
|
||||
case z.ZodTypes.void:
|
||||
if (parsedType !== ParsedType.undefined && parsedType !== ParsedType.null) {
|
||||
error.addError(makeError({ code: ZodErrorCode.invalid_type, expected: ParsedType.void, received: parsedType }));
|
||||
if (parsedType !== ZodParsedType.undefined && parsedType !== ZodParsedType.null) {
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ZodParsedType.void, received: parsedType }),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
break;
|
||||
case z.ZodTypes.array:
|
||||
if (parsedType !== ParsedType.array) {
|
||||
if (parsedType !== ZodParsedType.array) {
|
||||
// throw ZodError.fromString(`Non-array type: ${typeof obj}`);
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ParsedType.array, received: parsedType }),
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ZodParsedType.array, received: parsedType }),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
|
@ -244,10 +248,10 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
break;
|
||||
// return parsedArray as any;
|
||||
case z.ZodTypes.object:
|
||||
if (parsedType !== ParsedType.object) {
|
||||
if (parsedType !== ZodParsedType.object) {
|
||||
// throw ZodError.fromString(`Non-object type: ${typeof obj}`);
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ParsedType.object, received: parsedType }),
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ZodParsedType.object, received: parsedType }),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
|
@ -314,7 +318,7 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
error.addError(
|
||||
makeError({
|
||||
code: ZodErrorCode.invalid_union,
|
||||
suberrors: unionErrors,
|
||||
unionErrors: unionErrors,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
@ -350,17 +354,25 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
break;
|
||||
|
||||
case z.ZodTypes.tuple:
|
||||
if (parsedType !== ParsedType.array) {
|
||||
if (parsedType !== ZodParsedType.array) {
|
||||
// tupleError.addError('','Non-array type detected; invalid tuple.')
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ParsedType.array, received: parsedType }),
|
||||
makeError({ code: ZodErrorCode.invalid_type, expected: ZodParsedType.array, received: parsedType }),
|
||||
);
|
||||
throw error;
|
||||
// throw ZodError.fromString('Non-array type detected; invalid tuple.');
|
||||
}
|
||||
if (def.items.length !== obj.length) {
|
||||
if (obj.length > def.items.length) {
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.invalid_array_length, expected: def.items.length, received: obj.length }),
|
||||
makeError({ code: ZodErrorCode.too_big, maximum: def.items.length, inclusive: true, type: 'array' }),
|
||||
);
|
||||
// tupleError.addError('',`Incorrect number of elements in tuple: expected ${def.items.length}, got ${obj.length}`)
|
||||
// throw ZodError.fromString(
|
||||
// `Incorrect number of elements in tuple: expected ${def.items.length}, got ${obj.length}`,
|
||||
// );
|
||||
} else if (obj.length < def.items.length) {
|
||||
error.addError(
|
||||
makeError({ code: ZodErrorCode.too_small, minimum: def.items.length, inclusive: true, type: 'array' }),
|
||||
);
|
||||
// tupleError.addError('',`Incorrect number of elements in tuple: expected ${def.items.length}, got ${obj.length}`)
|
||||
// throw ZodError.fromString(
|
||||
|
@ -417,11 +429,11 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
// return obj;
|
||||
break;
|
||||
case z.ZodTypes.function:
|
||||
if (parsedType !== ParsedType.function) {
|
||||
if (parsedType !== ZodParsedType.function) {
|
||||
error.addError(
|
||||
makeError({
|
||||
code: ZodErrorCode.invalid_type,
|
||||
expected: ParsedType.function,
|
||||
expected: ZodParsedType.function,
|
||||
received: parsedType,
|
||||
}),
|
||||
);
|
||||
|
@ -437,7 +449,7 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
argsError.addError(
|
||||
makeError({
|
||||
code: ZodErrorCode.invalid_arguments,
|
||||
suberrors: [err],
|
||||
argumentsError: err,
|
||||
}),
|
||||
);
|
||||
throw argsError;
|
||||
|
@ -455,7 +467,7 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
returnsError.addError(
|
||||
makeError({
|
||||
code: ZodErrorCode.invalid_return_type,
|
||||
suberrors: [err],
|
||||
returnTypeError: err,
|
||||
}),
|
||||
);
|
||||
throw returnsError;
|
||||
|
@ -469,11 +481,11 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
return validatedFunc;
|
||||
// return obj;
|
||||
case z.ZodTypes.record:
|
||||
if (parsedType !== ParsedType.object) {
|
||||
if (parsedType !== ZodParsedType.object) {
|
||||
error.addError(
|
||||
makeError({
|
||||
code: ZodErrorCode.invalid_type,
|
||||
expected: ParsedType.object,
|
||||
expected: ZodParsedType.object,
|
||||
received: parsedType,
|
||||
}),
|
||||
);
|
||||
|
@ -501,7 +513,7 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
error.addError(
|
||||
makeError({
|
||||
code: ZodErrorCode.invalid_type,
|
||||
expected: ParsedType.date,
|
||||
expected: ZodParsedType.date,
|
||||
received: parsedType,
|
||||
}),
|
||||
);
|
||||
|
@ -523,11 +535,11 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
break;
|
||||
|
||||
case z.ZodTypes.promise:
|
||||
if (parsedType !== ParsedType.promise) {
|
||||
if (parsedType !== ZodParsedType.promise) {
|
||||
error.addError(
|
||||
makeError({
|
||||
code: ZodErrorCode.invalid_type,
|
||||
expected: ParsedType.promise,
|
||||
expected: ZodParsedType.promise,
|
||||
received: parsedType,
|
||||
}),
|
||||
);
|
||||
|
@ -568,14 +580,9 @@ export const ZodParser = (schemaDef: z.ZodTypeDef) => (
|
|||
const customChecks = def.checks || [];
|
||||
for (const check of customChecks) {
|
||||
if (!check.check(obj)) {
|
||||
error.addError(
|
||||
makeError({
|
||||
code: ZodErrorCode.custom_error,
|
||||
params: check.params,
|
||||
message: check.message,
|
||||
}),
|
||||
);
|
||||
// throw ZodError.fromString(check.message || `Failed custom check.`);
|
||||
const noMethodCheck = { ...check };
|
||||
delete noMethodCheck.check;
|
||||
error.addError(makeError(noMethodCheck));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import * as z from '.';
|
||||
|
||||
const run = async () => {
|
||||
const errorMap: z.ZodErrorMap = (error, ctx) => {
|
||||
/*
|
||||
|
||||
If error.message is set, that means the user is trying to
|
||||
override the error message. This is how method-specific
|
||||
error overrides work, like this:
|
||||
|
||||
z.string().min(5, { message: "TOO SMALL 🤬" })
|
||||
|
||||
It is a best practice to return `error.message` if it is set.
|
||||
|
||||
*/
|
||||
if (error.message) return { message: error.message };
|
||||
|
||||
/*
|
||||
This is where you override the various error codes
|
||||
*/
|
||||
switch (error.code) {
|
||||
case z.ZodErrorCode.invalid_type:
|
||||
if (error.expected === 'string') {
|
||||
return { message: `This ain't a string!` };
|
||||
}
|
||||
break;
|
||||
case z.ZodErrorCode.custom_error:
|
||||
// produce a custom message using error.params
|
||||
// error.params won't be set unless you passed
|
||||
// a `params` arguments into a custom validator
|
||||
const params = error.params || {};
|
||||
if (params.myField) {
|
||||
return { message: `Bad input: ${params.myField}` };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// fall back to default message!
|
||||
return { message: ctx.defaultError };
|
||||
};
|
||||
|
||||
try {
|
||||
z.string().parse(12, { errorMap });
|
||||
} catch (err) {
|
||||
console.log(JSON.stringify(err.errors, null, 2));
|
||||
}
|
||||
};
|
||||
|
||||
run();
|
|
@ -2,6 +2,7 @@ import * as z from './base';
|
|||
import { ZodUndefined } from './undefined';
|
||||
import { ZodNull } from './null';
|
||||
import { ZodUnion } from './union';
|
||||
import { ZodErrorCode } from '..';
|
||||
// import { maskUtil } from '../helpers/maskUtil';
|
||||
// import { zodmaskUtil } from '../helpers/zodmaskUtil';
|
||||
// import { applyMask } from '../masker';
|
||||
|
@ -29,14 +30,31 @@ export class ZodArray<T extends z.ZodTypeAny> extends z.ZodType<T['_type'][], Zo
|
|||
|
||||
nullable: () => ZodUnion<[this, ZodNull]> = () => ZodUnion.create([this, ZodNull.create()]);
|
||||
|
||||
min = (minLength: number, msg?: string) =>
|
||||
this.refine(data => data.length >= minLength, msg || `Array must contain ${minLength} or more items.`);
|
||||
min = (minLength: number, message?: string | { message?: string }) =>
|
||||
this._refinement({
|
||||
check: data => data.length >= minLength,
|
||||
code: ZodErrorCode.too_small,
|
||||
type: 'array',
|
||||
inclusive: true,
|
||||
minimum: minLength,
|
||||
...(typeof message === 'string' ? { message } : message),
|
||||
});
|
||||
|
||||
max = (maxLength: number, msg?: string) =>
|
||||
this.refine(data => data.length <= maxLength, msg || `Array must contain ${maxLength} or fewer items.`);
|
||||
// this.refine(data => data.length >= minLength, msg || `Array must contain ${minLength} or more items.`);
|
||||
|
||||
length = (len: number, msg?: string) =>
|
||||
this.refine(data => data.length == len, msg || `Array must contain ${len} items.`);
|
||||
max = (maxLength: number, message?: string | { message?: string }) =>
|
||||
this._refinement({
|
||||
check: data => data.length <= maxLength,
|
||||
code: ZodErrorCode.too_big,
|
||||
type: 'array',
|
||||
inclusive: true,
|
||||
maximum: maxLength,
|
||||
...(typeof message === 'string' ? { message } : message),
|
||||
});
|
||||
// this.refine(data => data.length <= maxLength, msg || `Array must contain ${maxLength} or fewer items.`);
|
||||
|
||||
length = (len: number, message?: string) => this.min(len, { message }).max(len, { message });
|
||||
// .refine(data => data.length === len, msg || `Array must contain ${len} items.`);
|
||||
|
||||
nonempty: () => ZodNonEmptyArray<T> = () => {
|
||||
return new ZodNonEmptyArray({ ...this._def, nonempty: true });
|
||||
|
@ -71,6 +89,29 @@ export class ZodNonEmptyArray<T extends z.ZodTypeAny> extends z.ZodType<[T['_typ
|
|||
|
||||
nullable: () => ZodUnion<[this, ZodNull]> = () => ZodUnion.create([this, ZodNull.create()]);
|
||||
|
||||
min = (minLength: number, message?: string | { message?: string }) =>
|
||||
this._refinement({
|
||||
check: data => data.length >= minLength,
|
||||
code: ZodErrorCode.too_small,
|
||||
minimum: minLength,
|
||||
type: 'array',
|
||||
inclusive: true,
|
||||
...(typeof message === 'string' ? { message } : message),
|
||||
});
|
||||
|
||||
// this.refine(data => data.length >= minLength, msg || `Array must contain ${minLength} or more items.`);
|
||||
|
||||
max = (maxLength: number, message?: string | { message?: string }) =>
|
||||
this._refinement({
|
||||
check: data => data.length >= maxLength,
|
||||
code: ZodErrorCode.too_big,
|
||||
maximum: maxLength,
|
||||
type: 'array',
|
||||
inclusive: true,
|
||||
...(typeof message === 'string' ? { message } : message),
|
||||
});
|
||||
|
||||
length = (len: number, message?: string) => this.min(len, { message }).max(len, { message });
|
||||
// static create = <T extends z.ZodTypeAny>(schema: T): ZodArray<T> => {
|
||||
// return new ZodArray({
|
||||
// t: z.ZodTypes.array,
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { ZodParser, ParseParams } from '../parser';
|
||||
import { ZodParser, ParseParams, MakeErrorData } from '../parser';
|
||||
import { util } from '../helpers/util';
|
||||
import { ZodErrorCode } from '..';
|
||||
|
||||
export enum ZodTypes {
|
||||
string = 'string',
|
||||
|
@ -32,15 +34,19 @@ export type ZodRawShape = { [k: string]: ZodTypeAny };
|
|||
// const asdf = { asdf: ZodString.create() };
|
||||
// type tset1 = typeof asdf extends ZodRawShape ? true :false
|
||||
|
||||
type InternalCheck<T> = {
|
||||
check: (arg: T) => any;
|
||||
} & MakeErrorData;
|
||||
|
||||
type Check<T> = {
|
||||
check: (arg: T) => any;
|
||||
message?: string;
|
||||
params?: { [k: string]: any };
|
||||
// code: string
|
||||
// code?: ZodErrorCode;
|
||||
};
|
||||
export interface ZodTypeDef {
|
||||
t: ZodTypes;
|
||||
checks?: Check<any>[];
|
||||
checks?: InternalCheck<any>[];
|
||||
}
|
||||
|
||||
export type TypeOf<T extends { _type: any }> = T['_type'];
|
||||
|
@ -62,6 +68,18 @@ export abstract class ZodType<Type, Def extends ZodTypeDef = ZodTypeDef> {
|
|||
|
||||
parse: (x: Type | unknown, params?: ParseParams) => Type;
|
||||
|
||||
parseAsync: (x: Type | unknown, params?: ParseParams) => Promise<Type> = value => {
|
||||
return new Promise((res, rej) => {
|
||||
try {
|
||||
const parsed = this.parse(value);
|
||||
return res(parsed);
|
||||
} catch (err) {
|
||||
// console.log(err);
|
||||
return rej(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
is(u: Type): u is Type {
|
||||
try {
|
||||
this.parse(u as any);
|
||||
|
@ -80,17 +98,21 @@ export abstract class ZodType<Type, Def extends ZodTypeDef = ZodTypeDef> {
|
|||
}
|
||||
}
|
||||
|
||||
refine = <Val extends (arg: Type) => any>(check: Val, message: string = 'Invalid value.') => {
|
||||
// const newChecks = [...this._def.checks || [], { check, message }];
|
||||
// console.log((this as any).constructor);
|
||||
return new (this as any).constructor({
|
||||
...this._def,
|
||||
checks: [...(this._def.checks || []), { check, message }],
|
||||
}) as this;
|
||||
// return this;
|
||||
refine = <Val extends (arg: Type) => any>(
|
||||
check: Val,
|
||||
message: string | util.Omit<Check<Type>, 'check'> = 'Invalid value.',
|
||||
) => {
|
||||
if (typeof message === 'string') {
|
||||
return this.refinement({ check, message });
|
||||
}
|
||||
return this.refinement({ check, ...message });
|
||||
};
|
||||
|
||||
refinement = (refinement: Check<Type>) => {
|
||||
return this._refinement({ code: ZodErrorCode.custom_error, ...refinement });
|
||||
};
|
||||
|
||||
protected _refinement: (refinement: InternalCheck<Type>) => this = refinement => {
|
||||
return new (this as any).constructor({
|
||||
...this._def,
|
||||
checks: [...(this._def.checks || []), refinement],
|
||||
|
|
|
@ -2,6 +2,8 @@ import * as z from './base';
|
|||
import { ZodUndefined } from './undefined';
|
||||
import { ZodNull } from './null';
|
||||
import { ZodUnion } from './union';
|
||||
import { ZodErrorCode } from '..';
|
||||
import { errorUtil } from '../helpers/errorUtil';
|
||||
|
||||
export interface ZodNumberDef extends z.ZodTypeDef {
|
||||
t: z.ZodTypes.number;
|
||||
|
@ -19,17 +21,79 @@ export class ZodNumber extends z.ZodType<number, ZodNumberDef> {
|
|||
});
|
||||
};
|
||||
|
||||
min = (minimum: number, msg?: string) => this.refine(data => data >= minimum, msg || `Value must be >= ${minimum}`);
|
||||
// min = (minimum: number, msg?: string) => this.refine(data => data >= minimum, msg || `Value must be >= ${minimum}`);
|
||||
min = (minimum: number, message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => data >= minimum,
|
||||
code: ZodErrorCode.too_small,
|
||||
minimum,
|
||||
type: 'number',
|
||||
inclusive: true,
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
|
||||
max = (maximum: number, msg?: string) => this.refine(data => data <= maximum, msg || `Value must be <= ${maximum}`);
|
||||
// max = (maximum: number, msg?: string) => this.refine(data => data <= maximum, msg || `Value must be <= ${maximum}`);
|
||||
max = (maximum: number, message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => data <= maximum,
|
||||
code: ZodErrorCode.too_big,
|
||||
maximum,
|
||||
type: 'number',
|
||||
inclusive: true,
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
|
||||
int = (msg?: string) => this.refine(data => Number.isInteger(data), msg || 'Value must be an integer.');
|
||||
// int = (msg?: string) => this.refine(data => Number.isInteger(data), msg || 'Value must be an integer.');
|
||||
int = (message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => Number.isInteger(data),
|
||||
code: ZodErrorCode.invalid_type,
|
||||
expected: 'integer',
|
||||
received: 'number',
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
|
||||
positive = (msg?: string) => this.refine(data => data > 0, msg || `Value must be positive`);
|
||||
// positive = (msg?: string) => this.refine(data => data > 0, msg || `Value must be positive`);
|
||||
positive = (message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => data > 0,
|
||||
code: ZodErrorCode.too_small,
|
||||
minimum: 0,
|
||||
type: 'number',
|
||||
inclusive: false,
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
|
||||
negative = (msg?: string) => this.refine(data => data < 0, msg || `Value must be negative`);
|
||||
// negative = (msg?: string) => this.refine(data => data < 0, msg || `Value must be negative`);
|
||||
negative = (message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => data < 0,
|
||||
code: ZodErrorCode.too_big,
|
||||
maximum: 0,
|
||||
type: 'number',
|
||||
inclusive: false,
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
|
||||
nonpositive = (msg?: string) => this.refine(data => data <= 0, msg || `Value must be non-positive`);
|
||||
// nonpositive = (msg?: string) => this.refine(data => data <= 0, msg || `Value must be non-positive`);
|
||||
nonpositive = (message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => data <= 0,
|
||||
code: ZodErrorCode.too_big,
|
||||
maximum: 0,
|
||||
type: 'number',
|
||||
inclusive: true,
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
|
||||
nonnegative = (msg?: string) => this.refine(data => data >= 0, msg || `Value must be non-negative`);
|
||||
// nonnegative = (msg?: string) => this.refine(data => data >= 0, msg || `Value must be non-negative`);
|
||||
nonnegative = (message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => data >= 0,
|
||||
code: ZodErrorCode.too_small,
|
||||
minimum: 0,
|
||||
type: 'number',
|
||||
inclusive: true,
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ import * as z from './base';
|
|||
import { ZodUndefined } from './undefined';
|
||||
import { ZodNull } from './null';
|
||||
import { ZodUnion } from './union';
|
||||
import { ZodErrorCode } from '..';
|
||||
import { errorUtil } from '../helpers/errorUtil';
|
||||
// import { ParseParams } from '../parser';
|
||||
|
||||
export interface ZodStringDef extends z.ZodTypeDef {
|
||||
|
@ -12,10 +14,9 @@ export interface ZodStringDef extends z.ZodTypeDef {
|
|||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
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;
|
||||
// eslint-disable-next-line
|
||||
const urlRegex = /^((https?|ftp):)?\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([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])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i;
|
||||
const uuidRegex = /([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}){1}/g;
|
||||
|
||||
export class ZodString extends z.ZodType<string, ZodStringDef> {
|
||||
optional: () => ZodUnion<[this, ZodUndefined]> = () => ZodUnion.create([this, ZodUndefined.create()]);
|
||||
|
@ -24,20 +25,59 @@ export class ZodString extends z.ZodType<string, ZodStringDef> {
|
|||
|
||||
toJSON = () => this._def;
|
||||
|
||||
min = (minLength: number, msg?: string) =>
|
||||
this.refine(data => data.length >= minLength, msg || `Value must be ${minLength} or more characters long`);
|
||||
min = (minLength: number, message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => data.length >= minLength,
|
||||
code: ZodErrorCode.too_small,
|
||||
minimum: minLength,
|
||||
type: 'string',
|
||||
inclusive: true,
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
// this.refine(data => data.length >= minLength, msg || `Value must be ${minLength} or more characters long`);
|
||||
|
||||
max = (maxLength: number, msg?: string) =>
|
||||
this.refine(data => data.length <= maxLength, msg || `Value must be ${maxLength} or fewer characters long`);
|
||||
max = (maxLength: number, message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => data.length <= maxLength,
|
||||
code: ZodErrorCode.too_big,
|
||||
maximum: maxLength,
|
||||
type: 'string',
|
||||
inclusive: true,
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
// max = (maxLength: number, msg?: string) =>
|
||||
// this.refine(data => data.length <= maxLength, msg || `Value must be ${maxLength} or fewer characters long`);
|
||||
length = (len: number, message?: errorUtil.ErrMessage) => this.min(len, message).max(len, message);
|
||||
// length = (len: number, msg?: string) =>
|
||||
// this.refine(data => data.length == len, msg || `Value must be ${len} characters long.`);
|
||||
|
||||
length = (len: number, msg?: string) =>
|
||||
this.refine(data => data.length == len, msg || `Value must be ${len} characters long.`);
|
||||
email = (message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => emailRegex.test(data),
|
||||
code: ZodErrorCode.invalid_string,
|
||||
validation: 'email',
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
//this.refine(data => emailRegex.test(data), errorUtil.errToObj(message));
|
||||
|
||||
email = (msg?: string) => this.refine(data => emailRegex.test(data), msg || 'Invalid email address.');
|
||||
url = (message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => urlRegex.test(data),
|
||||
code: ZodErrorCode.invalid_string,
|
||||
validation: 'url',
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
// url = (message?: errorUtil.ErrMessage) => this.refine(data => urlRegex.test(data), errorUtil.errToObj(message));
|
||||
|
||||
url = (msg?: string) => this.refine(data => urlRegex.test(data), msg || 'Invalid URL.');
|
||||
uuid = (message?: errorUtil.ErrMessage) =>
|
||||
this._refinement({
|
||||
check: data => uuidRegex.test(data),
|
||||
code: ZodErrorCode.invalid_string,
|
||||
validation: 'uuid',
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
|
||||
nonempty = (msg?: string) => this.refine(data => data.length > 0, msg || 'Value cannot be empty string');
|
||||
nonempty = (message?: errorUtil.ErrMessage) => this.min(1, errorUtil.errToObj(message));
|
||||
// validate = <Val extends (arg:this['_type'])=>boolean>(check:Val)=>{
|
||||
// const currChecks = this._def.validation.custom || [];
|
||||
// return new ZodString({
|
||||
|
|
|
@ -3695,10 +3695,10 @@ typedarray-to-buffer@^3.1.5:
|
|||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typescript@3.9:
|
||||
version "3.9.6"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"
|
||||
integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==
|
||||
typescript@3.2:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d"
|
||||
integrity sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==
|
||||
|
||||
undefsafe@^2.0.2:
|
||||
version "2.0.3"
|
||||
|
|
Loading…
Reference in New Issue