COnfigure docsify

This commit is contained in:
Colin McDonnell
2022-05-12 00:08:35 -07:00
parent 8dfb7b5e0d
commit d2607daee1
6 changed files with 181 additions and 2174 deletions
View File
+130 -133
View File
@@ -9,6 +9,7 @@
<a href="https://www.npmjs.com/package/zod" rel="nofollow"><img src="https://img.shields.io/npm/dw/zod.svg" alt="npm"></a> <a href="https://www.npmjs.com/package/zod" rel="nofollow"><img src="https://img.shields.io/npm/dw/zod.svg" alt="npm"></a>
<a href="https://www.npmjs.com/package/zod" rel="nofollow"><img src="https://img.shields.io/github/stars/colinhacks/zod" alt="stars"></a> <a href="https://www.npmjs.com/package/zod" rel="nofollow"><img src="https://img.shields.io/github/stars/colinhacks/zod" alt="stars"></a>
<a href="https://discord.gg/KaSRdyX2vc" rel="nofollow"><img src="https://img.shields.io/discord/893487829802418277?label=Discord&logo=discord&logoColor=white" alt="discord server"></a> <a href="https://discord.gg/KaSRdyX2vc" rel="nofollow"><img src="https://img.shields.io/discord/893487829802418277?label=Discord&logo=discord&logoColor=white" alt="discord server"></a>
<!-- <a href="https://app.netlify.com/sites/guileless-rolypoly-866f8a/deploys" rel="nofollow"><img src="https://api.netlify.com/api/v1/badges/0c13c2f2-86de-4115-895d-d0a4cb36af1a/deploy-status" alt="netlify deploy status"></a> -->
</p> </p>
<div align="center"> <div align="center">
@@ -28,11 +29,11 @@
These docs have been translated into [Chinese](./README_ZH.md). These docs have been translated into [Chinese](./README_ZH.md).
# Table of contents ## Table of contents
<!-- The full documentation is available both on the [official documentation site](https://zod.js.org/) (recommended) and in `README.md`. <!-- The full documentation is available both on the [official documentation site](https://zod.js.org/) (recommended) and in `README.md`.
### Go to [zod.js.org](https://zod.js.org) >> --> #### Go to [zod.js.org](https://zod.js.org) >> -->
- [What is Zod](#what-is-zod) - [What is Zod](#what-is-zod)
- [Installation](#installation) - [Installation](#installation)
@@ -70,7 +71,7 @@ These docs have been translated into [Chinese](./README_ZH.md).
- [Maps](#maps) - [Maps](#maps)
- [Sets](#sets) - [Sets](#sets)
- [Unions](#unions) - [Unions](#unions)
- [Discriminated Unions](#discriminated-unions) - [Discriminated Unions](#discriminated-unions)
- [Recursive types](#recursive-types) - [Recursive types](#recursive-types)
- [JSON type](#json-type) - [JSON type](#json-type)
- [Cyclical data](#cyclical-objects) - [Cyclical data](#cyclical-objects)
@@ -107,7 +108,7 @@ These docs have been translated into [Chinese](./README_ZH.md).
<!-- **Zod 2 is coming! Follow [@colinhacks](https://twitter.com/colinhacks) to stay updated and discuss the future of Zod.** --> <!-- **Zod 2 is coming! Follow [@colinhacks](https://twitter.com/colinhacks) to stay updated and discuss the future of Zod.** -->
# What is Zod ## What is Zod
Zod is a TypeScript-first schema declaration and validation library. I'm using the term "schema" to broadly refer to any data type, from a simple `string` to a complex nested object. Zod is a TypeScript-first schema declaration and validation library. I'm using the term "schema" to broadly refer to any data type, from a simple `string` to a complex nested object.
@@ -123,7 +124,7 @@ Some other great aspects:
- Functional approach: [parse, don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) - Functional approach: [parse, don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/)
- Works with plain JavaScript too! You don't need to use TypeScript. - Works with plain JavaScript too! You don't need to use TypeScript.
# Sponsorship ## Sponsorship
Sponsorship at any level is appreciated and encouraged. For individual developers, consider the [Cup of Coffee tier](https://github.com/sponsors/colinhacks). If you built a paid product using Zod, consider one of the [podium tiers](https://github.com/sponsors/colinhacks). Sponsorship at any level is appreciated and encouraged. For individual developers, consider the [Cup of Coffee tier](https://github.com/sponsors/colinhacks). If you built a paid product using Zod, consider one of the [podium tiers](https://github.com/sponsors/colinhacks).
@@ -242,7 +243,7 @@ Sponsorship at any level is appreciated and encouraged. For individual developer
</tr> </tr>
</table> </table>
# Installation ## Installation
To install Zod v3: To install Zod v3:
@@ -263,13 +264,13 @@ npm install zod
} }
``` ```
#### TypeScript requirements ### TypeScript requirements
- Zod 3.x requires TypeScript 4.1+ - Zod 3.x requires TypeScript 4.1+
- Zod 2.x requires TypeScript 3.7+ - Zod 2.x requires TypeScript 3.7+
- Zod 1.x requires TypeScript 3.3+ - Zod 1.x requires TypeScript 3.3+
# Ecosystem ## Ecosystem
There are a growing number of tools that are built atop or support Zod natively! If you've built a tool or library on top of Zod, tell me about it [on Twitter](https://twitter.com/colinhacks) or [start a Discussion](https://github.com/colinhacks/zod/discussions). I'll add it below and tweet it out. There are a growing number of tools that are built atop or support Zod natively! If you've built a tool or library on top of Zod, tell me about it [on Twitter](https://twitter.com/colinhacks) or [start a Discussion](https://github.com/colinhacks/zod/discussions). I'll add it below and tweet it out.
@@ -303,7 +304,7 @@ There are a growing number of tools that are built atop or support Zod natively!
- [`zod-formik-adapter`](https://github.com/robertLichtnow/zod-formik-adapter): A community-maintained Formik adapter for Zod - [`zod-formik-adapter`](https://github.com/robertLichtnow/zod-formik-adapter): A community-maintained Formik adapter for Zod
- [`react-zorm`](https://github.com/esamattis/react-zorm): Standalone `<form>` generation and validation for React using Zod - [`react-zorm`](https://github.com/esamattis/react-zorm): Standalone `<form>` generation and validation for React using Zod
# Basic usage ## Basic usage
Creating a simple string schema Creating a simple string schema
@@ -338,9 +339,9 @@ type User = z.infer<typeof User>;
// { username: string } // { username: string }
``` ```
# Defining schemas ## Defining schemas
## Primitives ### Primitives
```ts ```ts
import { z } from "zod"; import { z } from "zod";
@@ -367,7 +368,7 @@ z.unknown();
z.never(); z.never();
``` ```
## Literals ### Literals
```ts ```ts
const tuna = z.literal("tuna"); const tuna = z.literal("tuna");
@@ -380,7 +381,7 @@ tuna.value; // "tuna"
> Currently there is no support for Date or bigint literals in Zod. If you have a use case for this feature, please file an issue. > Currently there is no support for Date or bigint literals in Zod. If you have a use case for this feature, please file an issue.
## Strings ### Strings
Zod includes a handful of string-specific validations. Zod includes a handful of string-specific validations.
@@ -403,9 +404,7 @@ z.string().nonempty({ message: "Can't be empty" });
> Check out [validator.js](https://github.com/validatorjs/validator.js) for a bunch of other useful string validation functions. > Check out [validator.js](https://github.com/validatorjs/validator.js) for a bunch of other useful string validation functions.
#### Custom error messages You can customize some common errors messages when creating a string schema.
You can customize certain errors when creating a string schema.
```ts ```ts
const name = z.string({ const name = z.string({
@@ -425,7 +424,7 @@ z.string().url({ message: "Invalid url" });
z.string().uuid({ message: "Invalid UUID" }); z.string().uuid({ message: "Invalid UUID" });
``` ```
## Numbers ### Numbers
You can customize certain error messages when creating a number schema. You can customize certain error messages when creating a number schema.
@@ -460,7 +459,7 @@ Optionally, you can pass in a second argument to provide a custom error message.
z.number().lte(5, { message: "this👏is👏too👏big" }); z.number().lte(5, { message: "this👏is👏too👏big" });
``` ```
## NaNs ### NaNs
You can customize certain error messages when creating a nan schema. You can customize certain error messages when creating a nan schema.
@@ -471,7 +470,7 @@ const isNaN = z.nan({
}); });
``` ```
## Booleans ### Booleans
You can customize certain error messages when creating a boolean schema. You can customize certain error messages when creating a boolean schema.
@@ -482,7 +481,7 @@ const isActive = z.boolean({
}); });
``` ```
## Dates ### Dates
z.date() accepts a date, not a date string z.date() accepts a date, not a date string
@@ -504,7 +503,7 @@ dateSchema.safeParse(new Date("1/12/22")); // success: true
dateSchema.safeParse("2022-01-12T00:00:00.000Z"); // success: true dateSchema.safeParse("2022-01-12T00:00:00.000Z"); // success: true
``` ```
## Zod enums ### Zod enums
```ts ```ts
const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]); const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]);
@@ -549,7 +548,7 @@ You can also retrieve the list of options as a tuple with the `.options` propert
FishEnum.options; // ["Salmon", "Tuna", "Trout"]); FishEnum.options; // ["Salmon", "Tuna", "Trout"]);
``` ```
## Native enums ### Native enums
Zod enums are the recommended approach to defining and validating enums. But if you need to validate against an enum from a third-party library (or you don't want to rewrite your existing enums) you can use `z.nativeEnum()` . Zod enums are the recommended approach to defining and validating enums. But if you need to validate against an enum from a third-party library (or you don't want to rewrite your existing enums) you can use `z.nativeEnum()` .
@@ -617,9 +616,9 @@ You can access the underlying object with the `.enum` property:
FruitEnum.enum.Apple; // "apple" FruitEnum.enum.Apple; // "apple"
``` ```
## Optionals ### Optionals
You can make any schema optional with `z.optional()`: You can make any schema optional with `z.optional()`. This wraps the schema in a `ZodOptional` instance and returns the result.
```ts ```ts
const schema = z.optional(z.string()); const schema = z.optional(z.string());
@@ -628,7 +627,7 @@ schema.parse(undefined); // => returns undefined
type A = z.infer<typeof schema>; // string | undefined type A = z.infer<typeof schema>; // string | undefined
``` ```
You can make an existing schema optional with the `.optional()` method: For convenience, you can also call the `.optional()` method on an existing schema.
```ts ```ts
const user = z.object({ const user = z.object({
@@ -637,7 +636,7 @@ const user = z.object({
type C = z.infer<typeof user>; // { username?: string | undefined }; type C = z.infer<typeof user>; // { username?: string | undefined };
``` ```
#### `.unwrap` You can extract the wrapped schema from a `ZodOptional` instance with `.unwrap()`.
```ts ```ts
const stringSchema = z.string(); const stringSchema = z.string();
@@ -645,9 +644,9 @@ const optionalString = stringSchema.optional();
optionalString.unwrap() === stringSchema; // true optionalString.unwrap() === stringSchema; // true
``` ```
## Nullables ### Nullables
Similarly, you can create nullable types like so: Similarly, you can create nullable types with `z.nullable()`.
```ts ```ts
const nullableString = z.nullable(z.string()); const nullableString = z.nullable(z.string());
@@ -655,14 +654,14 @@ nullableString.parse("asdf"); // => "asdf"
nullableString.parse(null); // => null nullableString.parse(null); // => null
``` ```
You can make an existing schema nullable with the `nullable` method: Or use the `.nullable()` method.
```ts ```ts
const E = z.string().nullable(); // equivalent to D const E = z.string().nullable(); // equivalent to D
type E = z.infer<typeof E>; // string | null type E = z.infer<typeof E>; // string | null
``` ```
#### `.unwrap` Extract the inner schema with `.unwrap()`.
```ts ```ts
const stringSchema = z.string(); const stringSchema = z.string();
@@ -670,7 +669,7 @@ const nullableString = stringSchema.nullable();
nullableString.unwrap() === stringSchema; // true nullableString.unwrap() === stringSchema; // true
``` ```
## Objects ### Objects
```ts ```ts
// all properties are required by default // all properties are required by default
@@ -689,7 +688,7 @@ type Dog = {
}; };
``` ```
### `.shape` #### `.shape`
Use `.shape` to access the schemas for a particular key. Use `.shape` to access the schemas for a particular key.
@@ -698,7 +697,7 @@ Dog.shape.name; // => string schema
Dog.shape.age; // => number schema Dog.shape.age; // => number schema
``` ```
### `.extend` #### `.extend`
You can add additional fields an object schema with the `.extend` method. You can add additional fields an object schema with the `.extend` method.
@@ -710,7 +709,7 @@ const DogWithBreed = Dog.extend({
You can use `.extend` to overwrite fields! Be careful with this power! You can use `.extend` to overwrite fields! Be careful with this power!
### `.merge` #### `.merge`
Equivalent to `A.extend(B.shape)`. Equivalent to `A.extend(B.shape)`.
@@ -724,7 +723,7 @@ type Teacher = z.infer<typeof Teacher>; // => { students: string[], id: string }
> If the two schemas share keys, the properties of B overrides the property of A. The returned schema also inherits the "unknownKeys" policy (strip/strict/passthrough) and the catchall schema of B. > If the two schemas share keys, the properties of B overrides the property of A. The returned schema also inherits the "unknownKeys" policy (strip/strict/passthrough) and the catchall schema of B.
### `.pick/.omit` #### `.pick/.omit`
Inspired by TypeScript's built-in `Pick` and `Omit` utility types, all Zod object schemas have `.pick` and `.omit` methods that return a modified version. Consider this Recipe schema: Inspired by TypeScript's built-in `Pick` and `Omit` utility types, all Zod object schemas have `.pick` and `.omit` methods that return a modified version. Consider this Recipe schema:
@@ -753,7 +752,7 @@ type NoIDRecipe = z.infer<typeof NoIDRecipe>;
// => { name: string, ingredients: string[] } // => { name: string, ingredients: string[] }
``` ```
### `.partial` #### `.partial`
Inspired by the built-in TypeScript utility type [Partial](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialt), the `.partial` method makes all properties optional. Inspired by the built-in TypeScript utility type [Partial](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialt), the `.partial` method makes all properties optional.
@@ -788,7 +787,7 @@ const optionalEmail = user.partial({
*/ */
``` ```
### `.deepPartial` #### `.deepPartial`
The `.partial` method is shallow — it only applies one level deep. There is also a "deep" version: The `.partial` method is shallow — it only applies one level deep. There is also a "deep" version:
@@ -818,7 +817,7 @@ const deepPartialUser = user.deepPartial();
> Important limitation: deep partials only work as expected in hierarchies of objects, arrays, and tuples. > Important limitation: deep partials only work as expected in hierarchies of objects, arrays, and tuples.
#### Unrecognized keys #### `.passthrough`
By default Zod objects schemas strip out unrecognized keys during parsing. By default Zod objects schemas strip out unrecognized keys during parsing.
@@ -835,8 +834,6 @@ person.parse({
// extraKey has been stripped // extraKey has been stripped
``` ```
### `.passthrough`
Instead, if you want to pass through unknown keys, use `.passthrough()` . Instead, if you want to pass through unknown keys, use `.passthrough()` .
```ts ```ts
@@ -847,9 +844,9 @@ person.passthrough().parse({
// => { name: "bob dylan", extraKey: 61 } // => { name: "bob dylan", extraKey: 61 }
``` ```
### `.strict` #### `.strict`
You can _disallow_ unknown keys with `.strict()` . If there are any unknown keys in the input, Zod will throw an error. By default Zod objects schemas strip out unrecognized keys during parsing. You can _disallow_ unknown keys with `.strict()` . If there are any unknown keys in the input, Zod will throw an error.
```ts ```ts
const person = z const person = z
@@ -865,11 +862,11 @@ person.parse({
// => throws ZodError // => throws ZodError
``` ```
### `.strip` #### `.strip`
You can use the `.strip` method to reset an object schema to the default behavior (stripping unrecognized keys). You can use the `.strip` method to reset an object schema to the default behavior (stripping unrecognized keys).
### `.catchall` #### `.catchall`
You can pass a "catchall" schema into an object schema. All unknown keys will be validated against it. You can pass a "catchall" schema into an object schema. All unknown keys will be validated against it.
@@ -894,7 +891,7 @@ person.parse({
Using `.catchall()` obviates `.passthrough()` , `.strip()` , or `.strict()`. All keys are now considered "known". Using `.catchall()` obviates `.passthrough()` , `.strip()` , or `.strict()`. All keys are now considered "known".
## Arrays ### Arrays
```ts ```ts
const stringArray = z.array(z.string()); const stringArray = z.array(z.string());
@@ -910,7 +907,7 @@ z.string().optional().array(); // (string | undefined)[]
z.string().array().optional(); // string[] | undefined z.string().array().optional(); // string[] | undefined
``` ```
### `.element` #### `.element`
Use `.element` to access the schema for an element of the array. Use `.element` to access the schema for an element of the array.
@@ -918,7 +915,7 @@ Use `.element` to access the schema for an element of the array.
stringArray.element; // => string schema stringArray.element; // => string schema
``` ```
### `.nonempty` #### `.nonempty`
If you want to ensure that an array contains at least one element, use `.nonempty()`. If you want to ensure that an array contains at least one element, use `.nonempty()`.
@@ -940,7 +937,7 @@ const nonEmptyStrings = z.string().array().nonempty({
}); });
``` ```
### `.min/.max/.length` #### `.min/.max/.length`
```ts ```ts
z.string().array().min(5); // must contain 5 or more items z.string().array().min(5); // must contain 5 or more items
@@ -950,7 +947,7 @@ z.string().array().length(5); // must contain 5 items exactly
Unlike `.nonempty()` these methods do not change the inferred type. Unlike `.nonempty()` these methods do not change the inferred type.
## Tuples ### Tuples
Unlike arrays, tuples have a fixed number of elements and each element can have a different type. Unlike arrays, tuples have a fixed number of elements and each element can have a different type.
@@ -967,7 +964,7 @@ type Athlete = z.infer<typeof athleteSchema>;
// type Athlete = [string, number, { pointsScored: number }] // type Athlete = [string, number, { pointsScored: number }]
``` ```
## Unions ### Unions
Zod includes a built-in `z.union` method for composing "OR" types. Zod includes a built-in `z.union` method for composing "OR" types.
@@ -1005,7 +1002,7 @@ const item = z
.parse({ type: "a", a: "abc" }); .parse({ type: "a", a: "abc" });
``` ```
## Records ### Records
Record schemas are used to validate types such as `{ [k: string]: number }`. Record schemas are used to validate types such as `{ [k: string]: number }`.
@@ -1032,7 +1029,7 @@ userStore["77d2586b-9e8e-4ecf-8b21-ea7e0530eadd"] = {
}; // TypeError }; // TypeError
``` ```
#### A note on numerical keys **A note on numerical keys**
You may have expected `z.record()` to accept two arguments, one for the keys and one for the values. After all, TypeScript's built-in Record type does: `Record<KeyType, ValueType>` . Otherwise, how do you represent the TypeScript type `Record<number, any>` in Zod? You may have expected `z.record()` to accept two arguments, one for the keys and one for the values. After all, TypeScript's built-in Record type does: `Record<KeyType, ValueType>` . Otherwise, how do you represent the TypeScript type `Record<number, any>` in Zod?
@@ -1049,11 +1046,9 @@ for (const key in testMap) {
// prints: `1: string` // prints: `1: string`
``` ```
As you can see, JavaScript automatically casts all object keys to strings under the hood. As you can see, JavaScript automatically casts all object keys to strings under the hood. Since Zod is trying to bridge the gap between static and runtime types, it doesn't make sense to provide a way of creating a record schema with numerical keys, since there's no such thing as a numerical key in runtime JavaScript.
Since Zod is trying to bridge the gap between static and runtime types, it doesn't make sense to provide a way of creating a record schema with numerical keys, since there's no such thing as a numerical key in runtime JavaScript. ### Maps
## Maps
```ts ```ts
const stringNumberMap = z.map(z.string(), z.number()); const stringNumberMap = z.map(z.string(), z.number());
@@ -1062,7 +1057,7 @@ type StringNumberMap = z.infer<typeof stringNumberMap>;
// type StringNumberMap = Map<string, number> // type StringNumberMap = Map<string, number>
``` ```
## Sets ### Sets
```ts ```ts
const numberSet = z.set(z.number()); const numberSet = z.set(z.number());
@@ -1070,7 +1065,7 @@ type NumberSet = z.infer<typeof numberSet>;
// type NumberSet = Set<number> // type NumberSet = Set<number>
``` ```
### `.nonempty/.min/.max/.size` Set schemas can be further contrainted with the following utility methods.
```ts ```ts
z.set(z.string()).nonempty(); // must contain at least one item z.set(z.string()).nonempty(); // must contain at least one item
@@ -1079,9 +1074,7 @@ z.set(z.string()).max(5); // must contain 5 or fewer items
z.set(z.string()).size(5); // must contain 5 items exactly z.set(z.string()).size(5); // must contain 5 items exactly
``` ```
## Intersections ### Intersections
<!-- > ⚠️ Intersections are deprecated. If you are trying to merge objects, use the `.merge` method instead. -->
Intersections are useful for creating "logical AND" types. This is useful for intersecting two object types. Intersections are useful for creating "logical AND" types. This is useful for intersecting two object types.
@@ -1129,7 +1122,7 @@ type Teacher = z.infer<typeof Teacher>;
// { id:string; name:string }; // { id:string; name:string };
``` --> ``` -->
## Recursive types ### Recursive types
You can define a recursive schema in Zod, but because of a limitation of TypeScript, their type can't be statically inferred. Instead you'll need to define the type definition manually, and provide it to Zod as a "type hint". You can define a recursive schema in Zod, but because of a limitation of TypeScript, their type can't be statically inferred. Instead you'll need to define the type definition manually, and provide it to Zod as a "type hint".
@@ -1184,7 +1177,7 @@ const Category: z.ZodType<Category> = BaseCategory.merge(
); );
``` --> ``` -->
#### JSON type #### Validating JSON
If you want to validate any JSON value, you can use the snippet below. If you want to validate any JSON value, you can use the snippet below.
@@ -1205,7 +1198,7 @@ Thanks to [ggoodman](https://github.com/ggoodman) for suggesting this.
Despite supporting recursive schemas, passing cyclical data into Zod will cause an infinite loop. Despite supporting recursive schemas, passing cyclical data into Zod will cause an infinite loop.
## Promises ### Promises
```ts ```ts
const numberPromise = z.promise(z.number()); const numberPromise = z.promise(z.number());
@@ -1236,7 +1229,7 @@ const test = async () => {
When "parsing" a promise, Zod checks that the passed value is an object with `.then` and `.catch` methods — that's it. So you should be able to pass non-native Promises (Bluebird, etc) into `z.promise(...).parse` with no trouble. One gotcha: the return type of the parse function will be a _native_ `Promise` , so if you have downstream logic that uses non-standard Promise methods, this won't work. --> When "parsing" a promise, Zod checks that the passed value is an object with `.then` and `.catch` methods — that's it. So you should be able to pass non-native Promises (Bluebird, etc) into `z.promise(...).parse` with no trouble. One gotcha: the return type of the parse function will be a _native_ `Promise` , so if you have downstream logic that uses non-standard Promise methods, this won't work. -->
## Instanceof ### Instanceof
You can use `z.instanceof` to check that the input is an instance of a class. This is useful to validate inputs against classes that are exported from third-party libraries. You can use `z.instanceof` to check that the input is an instance of a class. This is useful to validate inputs against classes that are exported from third-party libraries.
@@ -1252,7 +1245,7 @@ TestSchema.parse(new Test()); // passes
TestSchema.parse("blob"); // throws TestSchema.parse("blob"); // throws
``` ```
## Function schemas ### Function schemas
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". 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".
@@ -1334,7 +1327,7 @@ const myFunction = z
myFunction; // (arg: string)=>number[] myFunction; // (arg: string)=>number[]
``` ```
## Preprocess ### Preprocess
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).) 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).)
@@ -1346,7 +1339,7 @@ const castToString = z.preprocess((val) => String(val), z.string());
This returns a `ZodEffects` instance. `ZodEffects` is a wrapper class that contains all logic pertaining to preprocessing, refinements, and transforms. This returns a `ZodEffects` instance. `ZodEffects` is a wrapper class that contains all logic pertaining to preprocessing, refinements, and transforms.
# ZodType: methods and properties ## ZodType: methods and properties
All Zod schemas contain certain methods. All Zod schemas contain certain methods.
@@ -1607,27 +1600,6 @@ stringToNumber.parse("string"); // => 6
> ⚠️ Transform functions must not throw. Make sure to use refinements before the transform or addIssue within the transform to make sure the input can be parsed by the transform. > ⚠️ Transform functions must not throw. Make sure to use refinements before the transform or addIssue within the transform to make sure the input can be parsed by the transform.
#### Validating during transform
Similar to `superRefine`, `transform` can optionally take a `ctx`. This allows you to simultaneously
validate and transform the value, which can be simpler than chaining `refine` and `validate`.
When calling `ctx.addIssue` make sure to still return a value of the correct type otherwise the inferred type will include `undefined`.
```ts
const Strings = z
.string()
.transform((val, ctx) => {
const parsed = parseInt(val);
if (isNaN(parsed)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Not a number",
});
}
return parsed;
});
```
#### Chaining order #### Chaining order
Note that `stringToNumber` above is an instance of the `ZodEffects` subclass. It is NOT an instance of `ZodString`. If you want to use the built-in methods of `ZodString` (e.g. `.email()`) you must apply those methods _before_ any transforms. Note that `stringToNumber` above is an instance of the `ZodEffects` subclass. It is NOT an instance of `ZodString`. If you want to use the built-in methods of `ZodString` (e.g. `.email()`) you must apply those methods _before_ any transforms.
@@ -1641,14 +1613,33 @@ const emailToDomain = z
emailToDomain.parse("[email protected]"); // => example.com emailToDomain.parse("[email protected]"); // => example.com
``` ```
#### Validating during transform
Similar to `superRefine`, `transform` can optionally take a `ctx`. This allows you to simultaneously validate and transform the value, which can be simpler than chaining `refine` and `validate`. When calling `ctx.addIssue` make sure to still return a value of the correct type otherwise the inferred type will include `undefined`.
```ts
const Strings = z.string().transform((val, ctx) => {
const parsed = parseInt(val);
if (isNaN(parsed)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Not a number",
});
}
return parsed;
});
```
#### Relationship to refinements #### Relationship to refinements
Transforms and refinements can be interleaved: Transforms and refinements can be interleaved. These will be executed in the order they are declared.
```ts ```ts
z.string() z.string()
.transform((val) => val.length) .transform((val) => val.toUpperCase())
.refine((val) => val > 25); .refine((val) => val.length > 15)
.transform((val) => `Hello ${val}`)
.refine((val) => val.indexOf("!") === -1);
``` ```
#### Async transforms #### Async transforms
@@ -1763,9 +1754,9 @@ z.object({ name: z.string() }).and(z.object({ age: z.number() })); // { name: st
z.intersection(z.object({ name: z.string() }), z.object({ age: z.number() })); z.intersection(z.object({ name: z.string() }), z.object({ age: z.number() }));
``` ```
# Guides and concepts ## Guides and concepts
## Type inference ### Type inference
You can extract the TypeScript type of any schema with `z.infer<typeof mySchema>` . You can extract the TypeScript type of any schema with `z.infer<typeof mySchema>` .
@@ -1777,7 +1768,7 @@ const u: A = 12; // TypeError
const u: A = "asdf"; // compiles const u: A = "asdf"; // compiles
``` ```
#### What about transforms? **What about transforms?**
In reality each Zod schema internally tracks **two** types: an input and an output. For most schemas (e.g. `z.string()`) these two are the same. But once you add transforms into the mix, these two values can diverge. For instance `z.string().transform(val => val.length)` has an input of `string` and an output of `number`. In reality each Zod schema internally tracks **two** types: an input and an output. For most schemas (e.g. `z.string()`) these two are the same. But once you add transforms into the mix, these two values can diverge. For instance `z.string().transform(val => val.length)` has an input of `string` and an output of `number`.
@@ -1794,7 +1785,41 @@ type output = z.output<typeof stringToNumber>; // number
type inferred = z.infer<typeof stringToNumber>; // number type inferred = z.infer<typeof stringToNumber>; // number
``` ```
## Writing generic functions ### Writing functions that accept Zod schemas
You can use `z.ZodType` as a type that will accept any Zod schema.
```ts
function runParse(schema: z.ZodType, input: unknown) {
return schema.parse(input);
}
```
The `ZodType` class has three generic parameters.
```ts
class ZodType<
Output = any,
Def extends ZodTypeDef = ZodTypeDef,
Input = Output
> { ... }
```
By constraining these in your generic input, you can limit what schemas are allowable as inputs to your function:
```ts
function makeSchemaOptional<T extends z.ZodType<string>>(schema: T) {
return schema.optional();
}
makeSchemaOptional(z.string());
// works fine
makeSchemaOptional(z.number());
// Error: 'ZodNumber' is not assignable to parameter of type 'ZodType<string, ZodTypeDef, string>'
```
### Writing generic functions
When attempting to write a functions that accepts a Zod schemas as an input, it's common to try something like this: When attempting to write a functions that accepts a Zod schemas as an input, it's common to try something like this:
@@ -1828,33 +1853,7 @@ const arg = makeSchemaOptional(z.string());
arg.unwrap(); // ZodString arg.unwrap(); // ZodString
``` ```
### Restricting valid schemas ### Error handling
The `ZodType` class has three generic parameters.
```ts
class ZodType<
Output,
Def extends ZodTypeDef = ZodTypeDef,
Input = Output
> { ... }
```
By constraining these in your generic input, you can limit what schemas are allowable as inputs to your function:
```ts
function makeSchemaOptional<T extends z.ZodType<string>>(schema: T) {
return schema.optional();
}
makeSchemaOptional(z.string());
// works fine
makeSchemaOptional(z.number());
// Error: 'ZodNumber' is not assignable to parameter of type 'ZodType<string, ZodTypeDef, string>'
```
## Error handling
Zod provides a subclass of Error called `ZodError`. ZodErrors contain an `issues` array containing detailed information about the validation problems. Zod provides a subclass of Error called `ZodError`. ZodErrors contain an `issues` array containing detailed information about the validation problems.
@@ -1875,7 +1874,7 @@ if (!data.success) {
} }
``` ```
#### Error formatting ### Error formatting
You can use the `.format()` method to convert this error into a nested object. You can use the `.format()` method to convert this error into a nested object.
@@ -1888,7 +1887,7 @@ data.error.format();
For detailed information about the possible error codes and how to customize error messages, check out the dedicated error handling guide: [ERROR_HANDLING.md](ERROR_HANDLING.md) For detailed information about the possible error codes and how to customize error messages, check out the dedicated error handling guide: [ERROR_HANDLING.md](ERROR_HANDLING.md)
# Comparison ## Comparison
There are a handful of other widely-used validation libraries, but all of them have certain design limitations that make for a non-ideal developer experience. There are a handful of other widely-used validation libraries, but all of them have certain design limitations that make for a non-ideal developer experience.
@@ -1940,20 +1939,18 @@ Branded -->
* Missing support for parsing cyclical data (maybe) * Missing support for parsing cyclical data (maybe)
* Missing error customization --> * Missing error customization -->
#### Joi **Joi**
[https://github.com/hapijs/joi](https://github.com/hapijs/joi) [https://github.com/hapijs/joi](https://github.com/hapijs/joi)
Doesn't support static type inference 😕 Doesn't support static type inference 😕
#### Yup **Yup**
[https://github.com/jquense/yup](https://github.com/jquense/yup) [https://github.com/jquense/yup](https://github.com/jquense/yup)
Yup is a full-featured library that was implemented first in vanilla JS, and later rewritten in TypeScript. Yup is a full-featured library that was implemented first in vanilla JS, and later rewritten in TypeScript.
Differences
- Supports casting and transforms - Supports casting and transforms
- All object fields are optional by default - All object fields are optional by default
- Missing object methods: (partial, deepPartial) - Missing object methods: (partial, deepPartial)
@@ -1964,7 +1961,7 @@ Differences
<!-- ¹Yup has a strange interpretation of the word `required`. Instead of meaning "not undefined", Yup uses it to mean "not empty". So `yup.string().required()` will not accept an empty string, and `yup.array(yup.string()).required()` will not accept an empty array. Instead, Yup us Zod arrays there is a dedicated `.nonempty()` method to indicate this, or you can implement it with a custom refinement. --> <!-- ¹Yup has a strange interpretation of the word `required`. Instead of meaning "not undefined", Yup uses it to mean "not empty". So `yup.string().required()` will not accept an empty string, and `yup.array(yup.string()).required()` will not accept an empty array. Instead, Yup us Zod arrays there is a dedicated `.nonempty()` method to indicate this, or you can implement it with a custom refinement. -->
#### io-ts **io-ts**
[https://github.com/gcanti/io-ts](https://github.com/gcanti/io-ts) [https://github.com/gcanti/io-ts](https://github.com/gcanti/io-ts)
@@ -2015,7 +2012,7 @@ This more declarative API makes schema definitions vastly more concise.
- Missing promise schemas - Missing promise schemas
- Missing function schemas - Missing function schemas
#### Runtypes **Runtypes**
[https://github.com/pelotom/runtypes](https://github.com/pelotom/runtypes) [https://github.com/pelotom/runtypes](https://github.com/pelotom/runtypes)
@@ -2028,7 +2025,7 @@ Good type inference support, but limited options for object type masking (no `.p
- Missing promise schemas - Missing promise schemas
- Missing error customization - Missing error customization
#### Ow **Ow**
[https://github.com/sindresorhus/ow](https://github.com/sindresorhus/ow) [https://github.com/sindresorhus/ow](https://github.com/sindresorhus/ow)
@@ -2036,6 +2033,6 @@ Ow is focused on function input validation. It's a library that makes it easy to
If you want to validate function inputs, use function schemas in Zod! It's a much simpler approach that lets you reuse a function type declaration without repeating yourself (namely, copy-pasting a bunch of ow assertions at the beginning of every function). Also Zod lets you validate your return types as well, so you can be sure there won't be any unexpected data passed downstream. If you want to validate function inputs, use function schemas in Zod! It's a much simpler approach that lets you reuse a function type declaration without repeating yourself (namely, copy-pasting a bunch of ow assertions at the beginning of every function). Also Zod lets you validate your return types as well, so you can be sure there won't be any unexpected data passed downstream.
# Changelog ## Changelog
View the changelog at [CHANGELOG.md](CHANGELOG.md) View the changelog at [CHANGELOG.md](CHANGELOG.md)
-2039
View File
File diff suppressed because it is too large Load Diff
+1
View File
File diff suppressed because one or more lines are too long
+19 -2
View File
@@ -13,6 +13,22 @@
rel="stylesheet" rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css"
/> />
<style>
.markdown-section {
max-width: 650px;
}
.markdown-section h2 {
padding: 40px 0px 10px 0px;
}
.markdown-section h3 {
padding: 0px 0px 10px 0px;
}
.markdown-section h4 {
font-size: 1rem;
padding: 40px 0px 5px 0px;
}
</style>
</head> </head>
<body> <body>
@@ -28,8 +44,9 @@
<script> <script>
window.$docsify = { window.$docsify = {
subMaxLevel: 1, subMaxLevel: 1,
onlyCover: true, maxLevel: 3,
// repo: 'docsifyjs/docsify', // onlyCover: true,
repo: "colinhacks/zod",
// coverpage: true, // coverpage: true,
}; };
</script> </script>
+31
View File
@@ -0,0 +1,31 @@
!(function (e) {
(e.languages.typescript = e.languages.extend("javascript", {
"class-name": {
pattern:
/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,
lookbehind: !0,
greedy: !0,
inside: null,
},
keyword:
/\b(?:abstract|as|asserts|async|await|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|null|of|package|private|protected|public|readonly|return|require|set|static|super|switch|this|throw|try|type|typeof|undefined|var|void|while|with|yield)\b/,
builtin:
/\b(?:string|Function|any|number|boolean|Array|symbol|console|Promise|unknown|never)\b/,
})),
delete e.languages.typescript.parameter;
var n = e.languages.extend("typescript", {});
delete n["class-name"],
(e.languages.typescript["class-name"].inside = n),
e.languages.insertBefore("typescript", "function", {
"generic-function": {
pattern:
/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,
greedy: !0,
inside: {
function: /^#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*/,
generic: { pattern: /<[\s\S]+/, alias: "class-name", inside: n },
},
},
}),
(e.languages.ts = e.languages.typescript);
})(Prism);