https://zod.dev
使用静态类型推理的打字稿优先架构验证
These docs have been translated into Chinese.
npm
(节点/bun)deno.land/x
).shape
.keyof
.extend
.merge
.pick/.omit
.partial
.deepPartial
.required
.passthrough
.strict
.strip
.catchall
.element
.nonempty
.min/.max/.length
.parse
.parseAsync
.safeParse
.safeParseAsync
.refine
.superRefine
.transform
.default
.describe
.catch
.optional
.nullable
.nullish
.array
.promise
.or
.and
.brand
.readonly
.pipe
.pipe()
来解决z.coerce
的常见问题。ZOD是一个打字稿优先的架构声明和验证库。我正在使用术语“架构”来广泛地指任何数据类型,从简单的string
到复杂的嵌套对象。
ZOD的设计原本是对开发人员的友好型。目的是消除重复类型的声明。使用ZOD,您可以声明验证器一次,ZOD将自动推断静态打字稿类型。将更简单的类型组合成复杂的数据结构很容易。
其他一些很棒的方面:
.optional()
)返回一个新实例在任何级别的赞助都受到赞赏和鼓励。如果您使用ZOD构建了付费产品,请考虑其中一个公司层。
最全面的用户管理平台
clerk.com
|
|
|
|
|
Propelauth | Cerbos | 标量 | trigger.dev |
Transloadit | 信息 | whop | cryptojobslist |
清楚的。 | 创新 | Storyblok | Mux |
布兰登·拜耳 | 吉伊·布拉贝克 | 亚历克斯·约翰逊 | 可函数系统 |
适应性 | Avana Wallet | Jason Lengstorf | 全球照明公司 |
主民 | 瑞安·帕尔默(Ryan Palmer) | 迈克尔·斯威尼 | NextBase |
依据 | 康纳·辛诺特(Connor Sinnott) | Mohammad-Ali A'râbi | supatool |
越来越多的工具在本地建立或支持ZOD! If you've built a tool or library on top of Zod, tell me about it on Twitter or start a Discussion.我将在下面添加并发推文。
tRPC
:构建无GraphQl的端到端TypeAfe API。@anatine/zod-nestjs
:用于在Nestjs项目中使用ZOD的帮助方法。zod-endpoints
:与ZOD的合同最严格键入端点。 OpenAPI兼容。zhttp
:一个具有ZOD输入和响应验证的OpenAPI兼容,严格键入的HTTP库。domain-functions
: Decouple your business logic from your framework using composable functions.通过ZOD架构驱动的一流类型推断。@zodios/core
:一个具有运行时的打字稿API客户端,并编译了由Axios和Zod支持的时间验证。express-zod-api
:构建具有I/O模式验证和自定义中间Wares的基于Express API。tapiduck
:带有ZOD和Express的端到端类型API;有点像TRPC,但更简单。koa-zod-router
:使用ZOD使用I/O验证在KOA中创建TypesAfe路由。zod-sockets
:带有I/O验证和内置异步规格的ZOD-Power socket.io Microframework react-hook-form
:一种用于React Hook形式的第一方ZOD解析器。zod-validation-error
:从ZodError
s生成用户友好的错误消息。zod-formik-adapter
: A community-maintained Formik adapter for Zod.react-zorm
:独立的<form>
使用ZOD的React的生成和验证。zodix
:混音加载程序和操作中的formdata和urlsearchParams的ZOD实用程序。conform
:一种类型AFFE形式验证库,用于逐步增强HTML形式。与Remix和Next.js一起使用remix-params-helper
:将ZOD与Remix应用程序的标准URLSearchParams和FormData简化整合。formik-validator-zod
:符合形式的验证器库,可简化使用zod和formik。zod-i18n-map
:可用于翻译ZOD错误消息。@modular-forms/solid
: Modular form library for SolidJS that supports Zod for validation.houseform
:使用ZOD进行验证的React表单库。sveltekit-superforms
:带有ZOD验证的Sveltekit的增压表单库。mobx-zod-form
:基于MOBX&ZOD的数据优先形式构建器。@vee-validate/zod
:带有ZOD架构验证的vue.js库。zod-form-renderer
: Auto-infer form fields from zod schema and render them with react-hook-form with E2E type safety. zod-to-ts
:从ZOD模式生成打字稿定义。zod-to-json-schema
: Convert your Zod schemas into JSON Schemas.@anatine/zod-openapi
:将ZOD架构转换为OpenAPI v3.x SchemaObject
。zod-fast-check
:从ZOD模式产生fast-check
任意。zod-dto
:从ZOD模式生成Nest.js DTO。fastify-type-provider-zod
:从ZOD模式创建快速类型提供商。zod-to-openapi
:从ZOD生成完整的OpenAPI(Swagger)文档,包括模式,端点和参数。nestjs-graphql-zod
:生成ZOD模式的Nestjs GraphQl模型类。提供使用ZOD架构的GraphQl方法装饰器。zod-openapi
:从Zod Schemas创建完整的OpenAPI v3.x文档。fastify-zod-openapi
:快速类型提供商,验证,序列化和 @fastify/swagger支持ZOD模式。typeschema
: Universal adapter for schema validation.zodex
:( DE)ZOD模式的序列化ts-to-zod
:将打字稿定义转换为ZOD模式。@runtyping/zod
:从静态类型和JSON模式生成ZOD。json-schema-to-zod
:将您的JSON模式转换为ZOD模式。现场演示。json-to-zod
:将JSON对象转换为ZOD模式。现场演示。graphql-codegen-typescript-validation-schema
:GraphQl Code Generator插件从您的GraphQl架构生成表单验证架构。zod-prisma
:从您的Prisma模式中生成ZOD模式。Supervillain
:从您的GO结构中生成ZOD模式。prisma-zod-generator
: Emit Zod schemas from your Prisma schema.drizzle-zod
:从毛毛雨架上发出ZOD模式。prisma-trpc-generator
:使用ZOD发射完全实现的TRPC路由器及其验证模式。zod-prisma-types
从Prisma模型中创建ZOD类型。quicktype
:将JSON对象和JSON Schemas转换为ZOD模式。@sanity-typed/zod
:从理智模式产生ZOD模式。java-to-zod
:将Pojos转换为Zod schemasOrval
:从OpenAPI模式生成ZOD模式Kubb
: Generate SDKs and Zod schemas from your OpenAPI schemas @anatine/zod-mock
:从ZOD模式生成模拟数据。由Faker.js提供支持。zod-mocking
:从ZOD模式中生成模拟数据。zod-fixture
:使用您的ZOD模式以确定性的方式自动化非相关测试固定装置的生成。zocker
:从您的模式中生成合理的模拟数据。zodock
基于ZOD模式生成模拟数据。zod-schema-faker
生成了来自Zod模式的模拟数据。由 @Faker-JS/Faker和Randexp.js提供支持freerstore
: Firestore cost optimizer.slonik
:Node.js Postgres客户端具有强大的ZOD集成。schemql
:通过将RAW SQL与目标类型安全性和架构验证相结合来增强您的SQL工作流程。soly
: Create CLI applications with zod.pastel
:使用React,Zod和Ink创建CLI应用程序。zod-xlsx
:使用ZOD模式的基于XLSX的资源验证器。znv
:带有ZOD模式的Node.js的类型安全环境解析和验证。zod-config
:具有灵活适配器的多个源的负载配置,可确保用ZOD键入安全性。unplugin-environment
:一个用于使用模式验证的环境变量加载的插件,使用虚拟模块,简单,具有IntelliSense的类型保护,以及更好的DX?。由Zod提供支持。 zod_utilz
:ZOD的框架不可知论。zod-playground
:学习和测试ZOD架构验证功能的工具。关联。zod-sandbox
:用于测试ZOD模式的受控环境。 Live demo.zod-dev
:有条件地禁用生产中的ZOD运行时解析。zod-accelerator
:加速Zod的吞吐量高达〜100倍。 打字稿4.5+!
您必须在tsconfig.json
中启用strict
模式。这是所有打字稿项目的最佳实践。
// tsconfig.json
{
// ...
"compilerOptions" : {
// ...
"strict" : true
}
}
npm
(节点/bun) npm install zod # npm
yarn add zod # yarn
bun add zod # bun
pnpm add zod # pnpm
ZOD还在每个提交上发布了金丝雀版本。安装金丝雀:
npm install zod@canary # npm
yarn add zod@canary # yarn
bun add zod@canary # bun
pnpm add zod@canary # pnpm
deno.land/x
)与节点不同,DENO依赖于直接URL导入,而不是像NPM这样的软件包管理器。 ZOD可在deno.land/x上找到。最新版本可以像这样导入:
import { z } from "https://deno.land/x/zod/mod.ts" ;
您还可以指定一个特定版本:
import { z } from "https://deno.land/x/[email protected]/mod.ts" ;
此读书我的其余部分假设您使用的是NPM并直接从
"zod"
软件包导入。
创建一个简单的字符串模式
import { z } from "zod" ;
// creating a schema for strings
const mySchema = z . string ( ) ;
// parsing
mySchema . parse ( "tuna" ) ; // => "tuna"
mySchema . parse ( 12 ) ; // => throws ZodError
// "safe" parsing (doesn't throw error if validation fails)
mySchema . safeParse ( "tuna" ) ; // => { success: true; data: "tuna" }
mySchema . safeParse ( 12 ) ; // => { success: false; error: ZodError }
创建对象模式
import { z } from "zod" ;
const User = z . object ( {
username : z . string ( ) ,
} ) ;
User . parse ( { username : "Ludwig" } ) ;
// extract the inferred type
type User = z . infer < typeof User > ;
// { username: string }
import { z } from "zod" ;
// primitive values
z . string ( ) ;
z . number ( ) ;
z . bigint ( ) ;
z . boolean ( ) ;
z . date ( ) ;
z . symbol ( ) ;
// empty types
z . undefined ( ) ;
z . null ( ) ;
z . void ( ) ; // accepts undefined
// catch-all types
// allows any value
z . any ( ) ;
z . unknown ( ) ;
// never type
// allows no values
z . never ( ) ;
Zod now provides a more convenient way to coerce primitive values.
const schema = z . coerce . string ( ) ;
schema . parse ( "tuna" ) ; // => "tuna"
schema . parse ( 12 ) ; // => "12"
在解析步骤中,输入通过String()
函数,该功能是将数据胁迫到字符串中的JavaScript内置。
schema . parse ( 12 ) ; // => "12"
schema . parse ( true ) ; // => "true"
schema . parse ( undefined ) ; // => "undefined"
schema . parse ( null ) ; // => "null"
The returned schema is a normal ZodString
instance so you can use all string methods.
z . coerce . string ( ) . email ( ) . min ( 5 ) ;
胁迫的工作原理
所有原始类型都支持强制。 ZOD使用内置构造函数胁迫所有输入: String(input)
, Number(input)
, new Date(input)
,等。
z . coerce . string ( ) ; // String(input)
z . coerce . number ( ) ; // Number(input)
z . coerce . boolean ( ) ; // Boolean(input)
z . coerce . bigint ( ) ; // BigInt(input)
z . coerce . date ( ) ; // new Date(input)
注意- 带有z.coerce.boolean()
布尔强制可能无法正常工作。任何真实的价值都被胁迫为true
,任何虚假的价值都被false
。
const schema = z . coerce . boolean ( ) ; // Boolean(input)
schema . parse ( "tuna" ) ; // => true
schema . parse ( "true" ) ; // => true
schema . parse ( "false" ) ; // => true
schema . parse ( 1 ) ; // => true
schema . parse ( [ ] ) ; // => true
schema . parse ( 0 ) ; // => false
schema . parse ( "" ) ; // => false
schema . parse ( undefined ) ; // => false
schema . parse ( null ) ; // => false
有关对强制逻辑的更多控制,请考虑使用z.preprocess
或z.pipe()
。
字面模式代表着一种字面类型,例如"hello world"
或5
。
const tuna = z . literal ( "tuna" ) ;
const twelve = z . literal ( 12 ) ;
const twobig = z . literal ( 2n ) ; // bigint literal
const tru = z . literal ( true ) ;
const terrificSymbol = Symbol ( "terrific" ) ;
const terrific = z . literal ( terrificSymbol ) ;
// retrieve literal value
tuna . value ; // "tuna"
目前,ZOD中没有对日期文字的支持。如果您有此功能的用例,请提交问题。
Zod includes a handful of string-specific validations.
// validations
z . string ( ) . max ( 5 ) ;
z . string ( ) . min ( 5 ) ;
z . string ( ) . length ( 5 ) ;
z . string ( ) . email ( ) ;
z . string ( ) . url ( ) ;
z . string ( ) . emoji ( ) ;
z . string ( ) . uuid ( ) ;
z . string ( ) . nanoid ( ) ;
z . string ( ) . cuid ( ) ;
z . string ( ) . cuid2 ( ) ;
z . string ( ) . ulid ( ) ;
z . string ( ) . regex ( regex ) ;
z . string ( ) . includes ( string ) ;
z . string ( ) . startsWith ( string ) ;
z . string ( ) . endsWith ( string ) ;
z . string ( ) . datetime ( ) ; // ISO 8601; by default only `Z` timezone allowed
z . string ( ) . ip ( ) ; // defaults to allow both IPv4 and IPv6
// transforms
z . string ( ) . trim ( ) ; // trim whitespace
z . string ( ) . toLowerCase ( ) ; // toLowerCase
z . string ( ) . toUpperCase ( ) ; // toUpperCase
// added in Zod 3.23
z . string ( ) . date ( ) ; // ISO date format (YYYY-MM-DD)
z . string ( ) . time ( ) ; // ISO time format (HH:mm:ss[.SSSSSS])
z . string ( ) . duration ( ) ; // ISO 8601 duration
z . string ( ) . base64 ( ) ;
请查看验证器,以获取许多其他有用的字符串验证功能,可以与改进结合使用。
You can customize some common error messages when creating a string schema.
const name = z . string ( {
required_error : "Name is required" ,
invalid_type_error : "Name must be a string" ,
} ) ;
使用验证方法时,您可以传递其他参数以提供自定义错误消息。
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 ( ) . emoji ( { message : "Contains non-emoji characters" } ) ;
z . string ( ) . uuid ( { message : "Invalid UUID" } ) ;
z . string ( ) . includes ( "tuna" , { message : "Must include tuna" } ) ;
z . string ( ) . startsWith ( "https://" , { message : "Must provide secure URL" } ) ;
z . string ( ) . endsWith ( ".com" , { message : "Only .com domains allowed" } ) ;
z . string ( ) . datetime ( { message : "Invalid datetime string! Must be UTC." } ) ;
z . string ( ) . date ( { message : "Invalid date string!" } ) ;
z . string ( ) . time ( { message : "Invalid time string!" } ) ;
z . string ( ) . ip ( { message : "Invalid IP address" } ) ;
As you may have noticed, Zod string includes a few date/time related validations.这些验证是基于正则表达式的,因此它们不像全日期/时间库那样严格。但是,它们非常方便验证用户输入。
z.string().datetime()
方法强制执行ISO 8601;默认值不是时区偏移和任意子秒十进制精度。
const datetime = z . string ( ) . datetime ( ) ;
datetime . parse ( "2020-01-01T00:00:00Z" ) ; // pass
datetime . parse ( "2020-01-01T00:00:00.123Z" ) ; // pass
datetime . parse ( "2020-01-01T00:00:00.123456Z" ) ; // pass (arbitrary precision)
datetime . parse ( "2020-01-01T00:00:00+02:00" ) ; // fail (no offsets allowed)
可以通过将offset
选项设置为true
来允许时区偏移。
const datetime = z . string ( ) . datetime ( { offset : true } ) ;
datetime . parse ( "2020-01-01T00:00:00+02:00" ) ; // pass
datetime . parse ( "2020-01-01T00:00:00.123+02:00" ) ; // pass (millis optional)
datetime . parse ( "2020-01-01T00:00:00.123+0200" ) ; // pass (millis optional)
datetime . parse ( "2020-01-01T00:00:00.123+02" ) ; // pass (only offset hours)
datetime . parse ( "2020-01-01T00:00:00Z" ) ; // pass (Z still supported)
您可以另外约束允许的precision
。默认情况下,支持任意的子秒精度(但可选)。
const datetime = z . string ( ) . datetime ( { precision : 3 } ) ;
datetime . parse ( "2020-01-01T00:00:00.123Z" ) ; // pass
datetime . parse ( "2020-01-01T00:00:00Z" ) ; // fail
datetime . parse ( "2020-01-01T00:00:00.123456Z" ) ; // fail
在ZOD 3.23中添加
z.string().date()
方法验证格式YYYY-MM-DD
的字符串。
const date = z . string ( ) . date ( ) ;
date . parse ( "2020-01-01" ) ; // pass
date . parse ( "2020-1-1" ) ; // fail
date . parse ( "2020-01-32" ) ; // fail
Added in Zod 3.23
z.string().time()
方法验证格式HH:MM:SS[.s+]
的字符串。第二个可以包括任意小数精度。它不允许任何形式的时区偏移。
const time = z . string ( ) . time ( ) ;
time . parse ( "00:00:00" ) ; // pass
time . parse ( "09:52:31" ) ; // pass
time . parse ( "23:59:59.9999999" ) ; // pass (arbitrary precision)
time . parse ( "00:00:00.123Z" ) ; // fail (no `Z` allowed)
time . parse ( "00:00:00.123+02:00" ) ; // fail (no offsets allowed)
您可以设置precision
选项以限制允许的小数精度。
const time = z . string ( ) . time ( { precision : 3 } ) ;
time . parse ( "00:00:00.123" ) ; // pass
time . parse ( "00:00:00.123456" ) ; // fail
time . parse ( "00:00:00" ) ; // fail
z.string().ip()
方法默认情况下validate ipv4和ipv6。
const ip = z . string ( ) . ip ( ) ;
ip . parse ( "192.168.1.1" ) ; // pass
ip . parse ( "84d5:51a0:9114:1855:4cfa:f2d7:1f12:7003" ) ; // pass
ip . parse ( "84d5:51a0:9114:1855:4cfa:f2d7:1f12:192.168.1.1" ) ; // pass
ip . parse ( "256.1.1.1" ) ; // fail
ip . parse ( "84d5:51a0:9114:gggg:4cfa:f2d7:1f12:7003" ) ; // fail
您可以另外设置IP version
。
const ipv4 = z . string ( ) . ip ( { version : "v4" } ) ;
ipv4 . parse ( "84d5:51a0:9114:1855:4cfa:f2d7:1f12:7003" ) ; // fail
const ipv6 = z . string ( ) . ip ( { version : "v6" } ) ;
ipv6 . parse ( "192.168.1.1" ) ; // fail
创建数字架构时,您可以自定义某些错误消息。
const age = z . number ( {
required_error : "Age is required" ,
invalid_type_error : "Age must be a number" ,
} ) ;
ZOD包含少数数字特定验证。
z . number ( ) . gt ( 5 ) ;
z . number ( ) . gte ( 5 ) ; // alias .min(5)
z . number ( ) . lt ( 5 ) ;
z . number ( ) . lte ( 5 ) ; // alias .max(5)
z . number ( ) . int ( ) ; // value must be an integer
z . number ( ) . positive ( ) ; // > 0
z . number ( ) . nonnegative ( ) ; // >= 0
z . number ( ) . negative ( ) ; // < 0
z . number ( ) . nonpositive ( ) ; // <= 0
z . number ( ) . multipleOf ( 5 ) ; // Evenly divisible by 5. Alias .step(5)
z . number ( ) . finite ( ) ; // value must be finite, not Infinity or -Infinity
z . number ( ) . safe ( ) ; // value must be between Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER
可选地,您可以传递第二个参数以提供自定义错误消息。
z . number ( ) . lte ( 5 , { message : "this?is?too?big" } ) ;
ZOD包括一些特定于Bigint的验证。
z . bigint ( ) . gt ( 5n ) ;
z . bigint ( ) . gte ( 5n ) ; // alias `.min(5n)`
z . bigint ( ) . lt ( 5n ) ;
z . bigint ( ) . lte ( 5n ) ; // alias `.max(5n)`
z . bigint ( ) . positive ( ) ; // > 0n
z . bigint ( ) . nonnegative ( ) ; // >= 0n
z . bigint ( ) . negative ( ) ; // < 0n
z . bigint ( ) . nonpositive ( ) ; // <= 0n
z . bigint ( ) . multipleOf ( 5n ) ; // Evenly divisible by 5n.
您可以在创建NAN模式时自定义某些错误消息。
const isNaN = z . nan ( {
required_error : "isNaN is required" ,
invalid_type_error : "isNaN must be 'not a number'" ,
} ) ;
创建布尔模式时,您可以自定义某些错误消息。
const isActive = z . boolean ( {
required_error : "isActive is required" ,
invalid_type_error : "isActive must be a boolean" ,
} ) ;
使用z.date()验证Date
实例。
z . date ( ) . safeParse ( new Date ( ) ) ; // success: true
z . date ( ) . safeParse ( "2022-01-12T00:00:00.000Z" ) ; // success: false
创建日期架构时,您可以自定义某些错误消息。
const myDateSchema = z . date ( {
required_error : "Please select a date and time" ,
invalid_type_error : "That's not a date!" ,
} ) ;
ZOD提供了一些特定于日期的验证。
z . date ( ) . min ( new Date ( "1900-01-01" ) , { message : "Too old" } ) ;
z . date ( ) . max ( new Date ( ) , { message : "Too young!" } ) ;
迄今为止的强迫
由于ZOD 3.20,请使用z.coerce.date()
通过new Date(input)
传递输入。
const dateSchema = z . coerce . date ( ) ;
type DateSchema = z . infer < typeof dateSchema > ;
// type DateSchema = Date
/* valid dates */
console . log ( dateSchema . safeParse ( "2023-01-10T00:00:00.000Z" ) . success ) ; // true
console . log ( dateSchema . safeParse ( "2023-01-10" ) . success ) ; // true
console . log ( dateSchema . safeParse ( "1/10/23" ) . success ) ; // true
console . log ( dateSchema . safeParse ( new Date ( "1/10/23" ) ) . success ) ; // true
/* invalid dates */
console . log ( dateSchema . safeParse ( "2023-13-10" ) . success ) ; // false
console . log ( dateSchema . safeParse ( "0000-00-00" ) . success ) ; // false
对于较旧的ZOD版本,请使用此线程中所述的z.preprocess
。
const FishEnum = z . enum ( [ "Salmon" , "Tuna" , "Trout" ] ) ;
type FishEnum = z . infer < typeof FishEnum > ;
// 'Salmon' | 'Tuna' | 'Trout'
z.enum
is a Zod-native way to declare a schema with a fixed set of allowable string values.将值的数组直接传递到z.enum()
。或者,用作as const
将枚举值定义为字符串元组。有关详细信息,请参见const主张文档。
const VALUES = [ "Salmon" , "Tuna" , "Trout" ] as const ;
const FishEnum = z . enum ( VALUES ) ;
这是不允许的,因为ZOD无法推断每个元素的确切值。
const fish = [ "Salmon" , "Tuna" , "Trout" ] ;
const FishEnum = z . enum ( fish ) ;
.enum
要获得带有ZOD枚举的自动完成,请使用模式的.enum
属性:
FishEnum . enum . Salmon ; // => autocompletes
FishEnum . enum ;
/*
=> {
Salmon: "Salmon",
Tuna: "Tuna",
Trout: "Trout",
}
*/
You can also retrieve the list of options as a tuple with the .options
property:
FishEnum . options ; // ["Salmon", "Tuna", "Trout"];
.exclude/.extract()
您可以使用.exclude
和.extract
方法创建ZOD枚举的子集。
const FishEnum = z . enum ( [ "Salmon" , "Tuna" , "Trout" ] ) ;
const SalmonAndTrout = FishEnum . extract ( [ "Salmon" , "Trout" ] ) ;
const TunaOnly = FishEnum . exclude ( [ "Salmon" , "Trout" ] ) ;
Zod enums are the recommended approach to defining and validating enums.但是,如果您需要对第三方库中的枚举进行验证(或者您不想重写现有的枚举),则可以使用z.nativeEnum()
。
数字枚举
enum Fruits {
Apple ,
Banana ,
}
const FruitEnum = z . nativeEnum ( Fruits ) ;
type FruitEnum = z . infer < typeof FruitEnum > ; // Fruits
FruitEnum . parse ( Fruits . Apple ) ; // passes
FruitEnum . parse ( Fruits . Banana ) ; // passes
FruitEnum . parse ( 0 ) ; // passes
FruitEnum . parse ( 1 ) ; // passes
FruitEnum . parse ( 3 ) ; // fails
字符串枚举
enum Fruits {
Apple = "apple" ,
Banana = "banana" ,
Cantaloupe , // you can mix numerical and string enums
}
const FruitEnum = z . nativeEnum ( Fruits ) ;
type FruitEnum = z . infer < typeof FruitEnum > ; // Fruits
FruitEnum . parse ( Fruits . Apple ) ; // passes
FruitEnum . parse ( Fruits . Cantaloupe ) ; // passes
FruitEnum . parse ( "apple" ) ; // passes
FruitEnum . parse ( "banana" ) ; // passes
FruitEnum . parse ( 0 ) ; // passes
FruitEnum . parse ( "Cantaloupe" ) ; // fails
枚举枚举
.nativeEnum()
函数也适用于as const
对象。as const
需要打字稿3.4+!
const Fruits = {
Apple : "apple" ,
Banana : "banana" ,
Cantaloupe : 3 ,
} as const ;
const FruitEnum = z . nativeEnum ( Fruits ) ;
type FruitEnum = z . infer < typeof FruitEnum > ; // "apple" | "banana" | 3
FruitEnum . parse ( "apple" ) ; // passes
FruitEnum . parse ( "banana" ) ; // passes
FruitEnum . parse ( 3 ) ; // passes
FruitEnum . parse ( "Cantaloupe" ) ; // fails
您可以使用.enum
属性访问基础对象:
FruitEnum . enum . Apple ; // "apple"
您可以使用z.optional()
进行任何架构可选。这将架构包裹在ZodOptional
实例中,并返回结果。
const schema = z . optional ( z . string ( ) ) ;
schema . parse ( undefined ) ; // => returns undefined
type A = z . infer < typeof schema > ; // string | undefined
为了方便起见,您还可以在现有架构上调用.optional()
方法。
const user = z . object ( {
username : z . string ( ) . optional ( ) ,
} ) ;
type C = z . infer < typeof user > ; // { username?: string | undefined };
You can extract the wrapped schema from a ZodOptional
instance with .unwrap()
.
const stringSchema = z . string ( ) ;
const optionalString = stringSchema . optional ( ) ;
optionalString . unwrap ( ) === stringSchema ; // true
同样,您可以使用z.nullable()
创建无效类型。
const nullableString = z . nullable ( z . string ( ) ) ;
nullableString . parse ( "asdf" ) ; // => "asdf"
nullableString . parse ( null ) ; // => null
或使用.nullable()
方法。
const E = z . string ( ) . nullable ( ) ; // equivalent to nullableString
type E = z . infer < typeof E > ; // string | null
用.unwrap()
提取内部模式。
const stringSchema = z . string ( ) ;
const nullableString = stringSchema . nullable ( ) ;
nullableString . unwrap ( ) === stringSchema ; // true
// all properties are required by default
const Dog = z . object ( {
name : z . string ( ) ,
age : z . number ( ) ,
} ) ;
// extract the inferred type like this
type Dog = z . infer < typeof Dog > ;
// equivalent to:
type Dog = {
name : string ;
age : number ;
} ;
.shape
使用.shape
访问特定键的模式。
Dog . shape . name ; // => string schema
Dog . shape . age ; // => number schema
.keyof
使用.keyof
从对象架构的键创建ZodEnum
架构。
const keySchema = Dog . keyof ( ) ;
keySchema ; // ZodEnum<["name", "age"]>
.extend
您可以使用.extend
方法将其他字段添加到对象架构中。
const DogWithBreed = Dog . extend ( {
breed : z . string ( ) ,
} ) ;
您可以使用.extend
来覆盖字段!小心这种力量!
.merge
相当于A.extend(B.shape)
。
const BaseTeacher = z . object ( { students : z . array ( z . string ( ) ) } ) ;
const HasID = z . object ( { id : z . string ( ) } ) ;
const Teacher = BaseTeacher . merge ( HasID ) ;
type Teacher = z . infer < typeof Teacher > ; // => { students: string[], id: string }
如果两个架构共享密钥,则B的属性覆盖了A的属性A。返回的模式也继承了“ Unknownekeys”策略(strip/strip/strict/passhrough)和B的Catchall模式。
.pick/.omit
受打字稿的内置Pick
和Omit
实用程序类型的启发,所有Zod Object schemas具有.pick
和.omit
方法返回修改版本。考虑此食谱模式:
const Recipe = z . object ( {
id : z . string ( ) ,
name : z . string ( ) ,
ingredients : z . array ( z . string ( ) ) ,
} ) ;
要仅保留某些键,请使用.pick
。
const JustTheName = Recipe . pick ( { name : true } ) ;
type JustTheName = z . infer < typeof JustTheName > ;
// => { name: string }
要删除某些键,请使用.omit
。
const NoIDRecipe = Recipe . omit ( { id : true } ) ;
type NoIDRecipe = z . infer < typeof NoIDRecipe > ;
// => { name: string, ingredients: string[] }
.partial
受内置的打字稿实用程序类型部分的启发, .partial
方法使所有属性都可选。
从这个对象开始:
const user = z . object ( {
email : z . string ( ) ,
username : z . string ( ) ,
} ) ;
// { email: string; username: string }
我们可以创建一个部分版本:
const partialUser = user . partial ( ) ;
// { email?: string | undefined; username?: string | undefined }
您还可以指定可选的属性:
const optionalEmail = user . partial ( {
email : true ,
} ) ;
/*
{
email?: string | undefined;
username: string
}
*/
.deepPartial
.partial
方法很浅 - 它仅适用一个深度。 There is also a "deep" version:
const user = z . object ( {
username : z . string ( ) ,
location : z . object ( {
latitude : z . number ( ) ,
longitude : z . number ( ) ,
} ) ,
strings : z . array ( z . object ( { value : z . string ( ) } ) ) ,
} ) ;
const deepPartialUser = user . deepPartial ( ) ;
/*
{
username?: string | undefined,
location?: {
latitude?: number | undefined;
longitude?: number | undefined;
} | undefined,
strings?: { value?: string}[]
}
*/
重要的限制:深层部分仅在对象,阵列和元素的层次结构中按预期工作。
.required
与.partial
方法相反, .required
方法使所有属性都需要。
从这个对象开始:
const user = z
. object ( {
email : z . string ( ) ,
username : z . string ( ) ,
} )
. partial ( ) ;
// { email?: string | undefined; username?: string | undefined }
我们可以创建一个必需的版本:
const requiredUser = user . required ( ) ;
// { email: string; username: string }
您还可以指定要制造的属性:
const requiredEmail = user . required ( {
email : true ,
} ) ;
/*
{
email: string;
username?: string | undefined;
}
*/
.passthrough
By default Zod object schemas strip out unrecognized keys during parsing.
const person = z . object ( {
name : z . string ( ) ,
} ) ;
person . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => { name: "bob dylan" }
// extraKey has been stripped
相反,如果您想通过未知键,请使用.passthrough()
。
person . passthrough ( ) . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => { name: "bob dylan", extraKey: 61 }
.strict
默认情况下,ZOD对象架构在解析过程中剥离未识别的键。 You can disallow unknown keys with .strict()
.如果输入中有任何未知键,ZOD会丢弃错误。
const person = z
. object ( {
name : z . string ( ) ,
} )
. strict ( ) ;
person . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => throws ZodError
.strip
您可以使用.strip
方法将对象架构重置为默认行为(剥离未识别的键)。
.catchall
您可以将“ cathall”架构传递到对象架构中。所有未知密钥都将被验证。
const person = z
. object ( {
name : z . string ( ) ,
} )
. catchall ( z . number ( ) ) ;
person . parse ( {
name : "bob dylan" ,
validExtraKey : 61 , // works fine
} ) ;
person . parse ( {
name : "bob dylan" ,
validExtraKey : false , // fails
} ) ;
// => throws ZodError
使用.catchall()
supiate .passthrough()
,. .strip()
或.strict()
。现在,所有键都被认为是“已知”。
const stringArray = z . array ( z . string ( ) ) ;
// equivalent
const stringArray = z . string ( ) . array ( ) ;
小心.array()
方法。它返回一个新的ZodArray
实例。这意味着您调用方法的顺序很重要。例如:
z . string ( ) . optional ( ) . array ( ) ; // (string | undefined)[]
z . string ( ) . array ( ) . optional ( ) ; // string[] | undefined
.element
Use .element
to access the schema for an element of the array.
stringArray . element ; // => string schema
.nonempty
如果要确保数组至少包含一个元素,请使用.nonempty()
。
const nonEmptyStrings = z . string ( ) . array ( ) . nonempty ( ) ;
// the inferred type is now
// [string, ...string[]]
nonEmptyStrings . parse ( [ ] ) ; // throws: "Array cannot be empty"
nonEmptyStrings . parse ( [ "Ariana Grande" ] ) ; // passes
您可以选择指定自定义错误消息:
// optional custom error message
const nonEmptyStrings = z . string ( ) . array ( ) . nonempty ( {
message : "Can't be empty!" ,
} ) ;
.min/.max/.length
z . string ( ) . array ( ) . min ( 5 ) ; // must contain 5 or more items
z . string ( ) . array ( ) . max ( 5 ) ; // must contain 5 or fewer items
z . string ( ) . array ( ) . length ( 5 ) ; // must contain 5 items exactly
与.nonempty()
不同,这些方法不会更改推断的类型。
与数组不同,元组具有固定数量的元素,每个元素都可以具有不同的类型。
const athleteSchema = z . tuple ( [
z . string ( ) , // name
z . number ( ) , // jersey number
z . object ( {
pointsScored : z . number ( ) ,
} ) , // statistics
] ) ;
type Athlete = z . infer < typeof athleteSchema > ;
// type Athlete = [string, number, { pointsScored: number }]
可以使用.rest
方法添加variadic(“ rest”)参数。
const variadicTuple = z . tuple ( [ z . string ( ) ] ) . rest ( z . number ( ) ) ;
const result = variadicTuple . parse ( [ "hello" , 1 , 2 , 3 ] ) ;
// => [string, ...number[]];
ZOD包括用于组成“或”类型的内置z.union
方法。
const stringOrNumber = z . union ( [ z . string ( ) , z . number ( ) ] ) ;
stringOrNumber . parse ( "foo" ) ; // passes
stringOrNumber . parse ( 14 ) ; // passes
ZOD将按顺序测试针对每个“选项”的输入,并返回成功验证的第一个值。
为了方便起见,您也可以使用.or
方法:
const stringOrNumber = z . string ( ) . or ( z . number ( ) ) ;
可选字符串验证:
要验证可选表单输入,您可以将所需的字符串验证与空字符串文字结合。
此示例验证了一个可选但需要包含有效URL的输入:
const optionalUrl = z . union ( [ z . string ( ) . url ( ) . nullish ( ) , z . literal ( "" ) ] ) ;
console . log ( optionalUrl . safeParse ( undefined ) . success ) ; // true
console . log ( optionalUrl . safeParse ( null ) . success ) ; // true
console . log ( optionalUrl . safeParse ( "" ) . success ) ; // true
console . log ( optionalUrl . safeParse ( "https://zod.dev" ) . success ) ; // true
console . log ( optionalUrl . safeParse ( "not a valid url" ) . success ) ; // false
一个有区别的联盟是对象模式的结合,它们都共享特定的密钥。
type MyUnion =
| { status : "success" ; data : string }
| { status : "failed" ; error : Error } ;
这样的工会可以用z.discriminatedUnion
方法表示。这可以更快地评估,因为ZOD可以检查歧视器密钥(上面的示例中的status
)以确定应使用哪个模式来解析输入。这使解析更有效,并让ZOD报告更友好的错误。
使用基本的联合方法,对每个提供的“选项”进行了测试,在无效的情况下,所有“选项”的问题在ZOD错误中显示。另一方面,歧视的联盟允许仅选择“选项”之一,对其进行测试,并仅显示与此“选项”相关的问题。
const myUnion = z . discriminatedUnion ( "status" , [
z . object ( { status : z . literal ( "success" ) , data : z . string ( ) } ) ,
z . object ( { status : z . literal ( "failed" ) , error : z . instanceof ( Error ) } ) ,
] ) ;
myUnion . parse ( { status : "success" , data : "yippie ki yay" } ) ;
您可以使用.options
属性提取对模式数组的引用。
myUnion . options ; // [ZodObject<...>, ZodObject<...>]
要合并两个或多个歧视的工会,请使用.options
进行破坏。
const A = z . discriminatedUnion ( "status" , [
/* options */
] ) ;
const B = z . discriminatedUnion ( "status" , [
/* options */
] ) ;
const AB = z . discriminatedUnion ( "status" , [ ... A . options , ... B . options ] ) ;
记录模式用于验证类型,例如Record<string, number>
。这对于通过ID存储或缓存项目特别有用。
const User = z . object ( { name : z . string ( ) } ) ;
const UserStore = z . record ( z . string ( ) , User ) ;
type UserStore = z . infer < typeof UserStore > ;
// => Record<string, { name: string }>
模式和推断类型可以像这样使用:
const userStore : UserStore = { } ;
userStore [ "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd" ] = {
name : "Carlotta" ,
} ; // passes
userStore [ "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd" ] = {
whatever : "Ice cream sundae" ,
} ; // TypeError
A note on numerical keys
尽管z.record(keyType, valueType)
能够接受数值键类型,并且Typescript的内置记录类型是Record<KeyType, ValueType>
,但很难表示Zod中的Typescript类型Record<number, any>
在ZOD中。
事实证明,围绕[k: number]
的打字稿行为有点不直观:
const testMap : { [ k : number ] : string } = {
1 : "one" ,
} ;
for ( const key in testMap ) {
console . log ( ` ${ key } : ${ typeof key } ` ) ;
}
// prints: `1: string`
如您所见,JavaScript自动将所有对象键施放在引擎盖下的字符串。由于ZOD试图弥合静态和运行时类型之间的差距,因此提供一种使用数值键创建记录架构的方法是没有意义的,因为在运行时JavaScript中没有数值键。
const stringNumberMap = z . map ( z . string ( ) , z . number ( ) ) ;
type StringNumberMap = z . infer < typeof stringNumberMap > ;
// type StringNumberMap = Map<string, number>
const numberSet = z . set ( z . number ( ) ) ;
type NumberSet = z . infer < typeof numberSet > ;
// type NumberSet = Set<number>
设置模式可以通过以下实用程序方法进一步限制。
z . set ( z . string ( ) ) . nonempty ( ) ; // must contain at least one item
z . set ( z . string ( ) ) . min ( 5 ) ; // must contain 5 or more items
z . set ( z . string ( ) ) . max ( 5 ) ; // must contain 5 or fewer items
z . set ( z . string ( ) ) . size ( 5 ) ; // must contain 5 items exactly
交集对于创建“逻辑和类型”很有用。这对于与两种对象类型相交很有用。
const Person = z . object ( {
name : z . string ( ) ,
} ) ;
const Employee = z . object ( {
role : z . string ( ) ,
} ) ;
const EmployedPerson = z . intersection ( Person , Employee ) ;
// equivalent to:
const EmployedPerson = Person . and ( Employee ) ;
尽管在许多情况下,建议使用A.merge(B)
合并两个对象。 .merge
方法返回一个新的ZodObject
实例,而A.and(B)
返回一个不太有用的ZodIntersection
实例,该实例缺少诸如pick
and omit
之类的常见对象方法。
const a = z . union ( [ z . number ( ) , z . string ( ) ] ) ;
const b = z . union ( [ z . number ( ) , z . boolean ( ) ] ) ;
const c = z . intersection ( a , b ) ;
type c = z . infer < typeof c > ; // => number
您可以在ZOD中定义递归架构,但是由于打字稿的限制,无法静态地推断出它们的类型。取而代之的是,您需要手动定义类型定义,并将其作为“类型提示”提供给ZOD。
const baseCategorySchema = z . object ( {
name : z . string ( ) ,
} ) ;
type Category = z . infer < typeof baseCategorySchema > & {
subcategories : Category [ ] ;
} ;
const categorySchema : z . ZodType < Category > = baseCategorySchema . extend ( {
subcategories : z . lazy ( ( ) => categorySchema . array ( ) ) ,
} ) ;
categorySchema . parse ( {
name : "People" ,
subcategories : [
{
name : "Politicians" ,
subcategories : [
{
name : "Presidents" ,
subcategories : [ ] ,
} ,
] ,
} ,
] ,
} ) ; // passes
Thanks to crasite for this example.
当使用z.ZodType
与z.ZodEffects
( .refine
, .transform
, preprocess
等...)时,您将需要定义架构的输入和输出类型。 z.ZodType<Output, z.ZodTypeDef, Input>
const isValidId = ( id : string ) : id is `${ string } /${ string } ` =>
id . split ( "/" ) . length === 2 ;
const baseSchema = z . object ( {
id : z . string ( ) . refine ( isValidId ) ,
} ) ;
type Input = z . input < typeof baseSchema > & {
children : Input [ ] ;
} ;
type Output = z . output < typeof baseSchema > & {
children : Output [ ] ;
} ;
const schema : z . ZodType < Output , z . ZodTypeDef , Input > = baseSchema . extend ( {
children : z . lazy ( ( ) => schema . array ( ) ) ,
} ) ;
Thanks to marcus13371337 and JoelBeeldi for this example.
如果要验证任何JSON值,则可以使用下面的摘要。
const literalSchema = z . union ( [ z . string ( ) , z . number ( ) , z . boolean ( ) , z . null ( ) ] ) ;
type Literal = z . infer < typeof literalSchema > ;
type Json = Literal | { [ key : string ] : Json } | Json [ ] ;
const jsonSchema : z . ZodType < Json > = z . lazy ( ( ) =>
z . union ( [ literalSchema , z . array ( jsonSchema ) , z . record ( jsonSchema ) ] )
) ;
jsonSchema . parse ( data ) ;
感谢Ggoodman提出的建议。
尽管支持递归模式,但在某些情况下,将周期性数据传递到ZOD会导致无限环路。
要在造成问题之前检测周期性对象,请考虑这种方法。
const numberPromise = z . promise ( z . number ( ) ) ;
“解析”与Promise模式有所不同。验证分为两个部分:
.then
带有.catch
方法的对象。)。.then
to attach an additional validation step onto the existing Promise.您必须在返回的承诺上使用.catch
来处理验证失败。 numberPromise . parse ( "tuna" ) ;
// ZodError: Non-Promise type: string
numberPromise . parse ( Promise . resolve ( "tuna" ) ) ;
// => Promise<number>
const test = async ( ) => {
await numberPromise . parse ( Promise . resolve ( "tuna" ) ) ;
// ZodError: Non-number type: string
await numberPromise . parse ( Promise . resolve ( 3.14 ) ) ;
// => 3.14
} ;
您可以使用z.instanceof
来检查输入是否是类的实例。这对于验证从第三方库导出的类验证输入很有用。
class Test {
name : string ;
}
const TestSchema = z . instanceof ( Test ) ;
const blob : any = "whatever" ;
TestSchema . parse ( new Test ( ) ) ; // passes
TestSchema . parse ( blob ) ; // throws
ZOD还可以使您定义“函数架构”。这使得易于验证函数的输入和输出,而无需将验证代码和“业务逻辑”相结合。
您可以使用z.function(args, returnType)
创建功能架构。
const myFunction = z . function ( ) ;
type myFunction = z . infer < typeof myFunction > ;
// => ()=>unknown
定义输入和输出。
const myFunction = z
. function ( )
. args ( z . string ( ) , z . number ( ) ) // accepts an arbitrary number of arguments
. returns ( z . boolean ( ) ) ;
type myFunction = z . infer < typeof myFunction > ;
// => (arg0: string, arg1: number)=>boolean
函数模式具有.implement()
方法,该方法接受一个函数并返回一个新功能,该函数自动验证其输入和输出。
const trimmedLength = z
. function ( )
. args ( z . string ( ) ) // accepts an arbitrary number of arguments
. returns ( z . number ( ) )
. implement ( ( x ) => {
// TypeScript knows x is a string!
return x . trim ( ) . length ;
} ) ;
trimmedLength ( "sandwich" ) ; // => 8
trimmedLength ( " asdf " ) ; // => 4
如果您只关心验证输入,则不要调用.returns()
方法。输出类型将从实现中推断出来。
如果您的功能不返回任何内容,则可以使用特殊
z.void()
选项。这将使ZOD正确推断出返回函数的类型。 (Void-returning functions actually return undefined.)
const myFunction = z
. function ( )
. args ( z . string ( ) )
. implement ( ( arg ) => {
return [ arg . length ] ;
} ) ;
myFunction ; // (arg: string)=>number[]
从功能模式中提取输入和输出模式。
myFunction . parameters ( ) ;
// => ZodTuple<[ZodString, ZodNumber]>
myFunction . returnType ( ) ;
// => ZodBoolean
ZOD现在支持原始胁迫,而无需
.preprocess()
。有关更多信息,请参见“胁迫文档”。
通常,ZOD在“解析然后转换”范式下运行。 ZOD首先验证输入,然后将其通过一系列变换函数。 (有关转换的更多信息,请阅读.Transform文档。)
但是有时您想在解析发生之前对输入进行一些转换。 A common use case: type coercion. ZOD可以使用z.preprocess()
启用此功能。
const castToString = z . preprocess ( ( val ) => String ( val ) , z . string ( ) ) ;
这将返回ZodEffects
实例。 ZodEffects
是一个包装类别,包含与预处理,改进和转换有关的所有逻辑。
您可以使用z.custom()
为任何打字类型创建ZOD模式。这对于为框不支持ZOD支持的类型(例如Template String文字)创建模式很有用。
const px = z . custom < `${ number } px` > ( ( val ) => {
return typeof val === "string" ? / ^d+px$ / . test ( val ) : false ;
} ) ;
type px = z . infer < typeof px > ; // `${number}px`
px . parse ( "42px" ) ; // "42px"
px . parse ( "42vw" ) ; // throws;
如果您不提供验证功能,ZOD将允许任何值。这可能很危险!
z . custom < { arg : string } > ( ) ; // performs no validation
您可以通过传递第二个参数来自定义错误消息和其他选项。 This parameter works the same way as the params parameter of .refine
.
z . custom < ... > ( ( val ) => ... , "custom error message" ) ;
所有ZOD模式都包含某些方法。
.parse
.parse(data: unknown): T
Given any Zod schema, you can call its .parse
method to check data
is valid.如果是这样,则返回一个带有完整类型信息的值!否则,会丢弃错误。
重要的是:
.parse
返回的值是您传递的变量的深度克隆。
const stringSchema = z . string ( ) ;
stringSchema . parse ( "fish" ) ; // => returns "fish"
stringSchema . parse ( 12 ) ; // throws error
.parseAsync
.parseAsync(data:unknown): Promise<T>
如果您使用异步改进或转换(稍后再进行更多),则需要使用.parseAsync
。
const stringSchema = z . string ( ) . refine ( async ( val ) => val . length <= 8 ) ;
await stringSchema . parseAsync ( "hello" ) ; // => returns "hello"
await stringSchema . parseAsync ( "hello world" ) ; // => throws error
.safeParse
.safeParse(data:unknown): { success: true; data: T; } | { success: false; error: ZodError; }
如果您不希望ZOD在验证失败时丢弃错误,请使用.safeParse
。此方法返回包含成功解析数据或Zoderror实例的对象,其中包含有关验证问题的详细信息。
stringSchema . safeParse ( 12 ) ;
// => { success: false; error: ZodError }
stringSchema . safeParse ( "billie" ) ;
// => { success: true; data: 'billie' }
结果是一个有区别的联盟,因此您可以非常方便地处理错误:
const result = stringSchema . safeParse ( "billie" ) ;
if ( ! result . success ) {
// handle error then return
result . error ;
} else {
// do something
result . data ;
}
.safeParseAsync
别名:
.spa
safeParse
的异步版本。
await stringSchema . safeParseAsync ( "billie" ) ;
为了方便起见,这是对.spa
的混音:
await stringSchema . spa ( "billie" ) ;
.refine
.refine(validator: (data:T)=>any, params?: RefineParams)
Zod lets you provide custom validation logic via refinements . (有关创建多个问题和自定义错误代码等高级功能,请参见.superRefine
。)
ZOD旨在尽可能接近镜像。但是,您可能希望检查许多所谓的“改进类型”。例如:检查一个数字是一个整数,或者字符串是有效的电子邮件地址。
例如,您可以使用.refine
上的任何ZOD架构上定义自定义验证:
const myString = z . string ( ) . refine ( ( val ) => val . length <= 255 , {
message : "String can't be more than 255 characters" ,
} ) ;
配x 改进功能不应投掷。相反,他们应该将虚假的值归还信号故障。
如您所见, .refine
需要两个参数。
T
型 - 示例的推断类型),并返回any
。任何真实价值都将通过验证。 (Prior to [email protected] the validation function had to return a boolean.) type RefineParams = {
// override error message
message ?: string ;
// appended to error path
path ?: ( string | number ) [ ] ;
// params object you can use to customize message
// in error map
params ?: object ;
} ;
对于高级案例,第二个参数也可以是返回RefineParams
的函数。
const longString = z . string ( ) . refine (
( val ) => val . length > 10 ,
( val ) => ( { message : ` ${ val } is not more than 10 characters` } )
) ;
const passwordForm = z
. object ( {
password : z . string ( ) ,
confirm : z . string ( ) ,
} )
. refine ( ( data ) => data . password === data . confirm , {
message : "Passwords don't match" ,
path : [ "confirm" ] , // path of error
} ) ;
passwordForm . parse ( { password : "asdf" , confirm : "qwer" } ) ;
因为您提供了一个path
参数,因此结果错误将为:
ZodError {
issues : [ {
"code" : "custom" ,
"path" : [ "confirm" ] ,
"message" : "Passwords don't match"
} ]
}
改进也可能是异步:
const userId = z . string ( ) . refine ( async ( id ) => {
// verify that ID exists in database
return true ;
} ) ;
配x 如果使用异步改进,则必须使用.parseAsync
方法来解析数据! Otherwise Zod will throw an error.
变换和改进可以交错:
z . string ( )
. transform ( ( val ) => val . length )
. refine ( ( val ) => val > 25 ) ;
.superRefine
.refine
方法实际上是一种称为superRefine
的更通用(和详细)方法的句法糖。这是一个例子:
const Strings = z . array ( z . string ( ) ) . superRefine ( ( val , ctx ) => {
if ( val . length > 3 ) {
ctx . addIssue ( {
code : z . ZodIssueCode . too_big ,
maximum : 3 ,
type : "array" ,
inclusive : true ,
message : "Too many items ?" ,
} ) ;
}
if ( val . length !== new Set ( val ) . size ) {
ctx . addIssue ( {
code : z . ZodIssueCode . custom ,
message : `No duplicates allowed.` ,
} ) ;
}
} ) ;
您可以根据需要添加尽可能多的问题。如果在函数执行过程中未调用ctx.addIssue
,则验证通过。
通常,改进始终会使用ZodIssueCode.custom
错误代码创建问题,但是使用superRefine
可以提出任何ZodIssueCode
的问题。每个问题代码在错误处理指南中详细描述:error_handling.md。
默认情况下,即使在完善检查失败后,解析也将继续。例如,如果您将多个修补链链在一起,则将全部执行。 However, it may be desirable to abort early to prevent later refinements from being executed.为了实现这一目标,请将fatal
标志传递给ctx.addIssue
,然后返回z.NEVER
。
const schema = z . number ( ) . superRefine ( ( val , ctx ) => {
if ( val < 10 ) {
ctx . addIssue ( {
code : z . ZodIssueCode . custom ,
message : "should be >= 10" ,
fatal : true ,
} ) ;
return z . NEVER ;
}
if ( val !== 12 ) {
ctx . addIssue ( {
code : z . ZodIssueCode . custom ,
message : "should be twelve" ,
} ) ;
}
} ) ;
If you provide a type predicate to .refine()
or .superRefine()
, the resulting type will be narrowed down to your predicate's type.如果您要混合多个链式的改进和转换,这将很有用:
const schema = z
. object ( {
first : z . string ( ) ,
second : z . number ( ) ,
} )
. nullable ( )
. superRefine ( ( arg , ctx ) : arg is { first : string ; second : number } => {
if ( ! arg ) {
ctx . addIssue ( {
code : z . ZodIssueCode . custom , // customize your issue
message : "object should exist" ,
} ) ;
}
return z . NEVER ; // The return value is not used, but we need to return something to satisfy the typing
} )
// here, TS knows that arg is not null
. refine ( ( arg ) => arg . first === "bob" , "`first` is not `bob`!" ) ;
配x 您必须使用ctx.addIssue()
而不是返回布尔值来指示验证是否通过。如果在函数执行过程中未调用ctx.addIssue
,则验证通过。
.transform
要在解析后转换数据,请使用transform
方法。
const stringToNumber = z . string ( ) . transform ( ( val ) => val . length ) ;
stringToNumber . parse ( "string" ) ; // => 6
请注意,上面的stringToNumber
是ZodEffects
子类的实例。这不是ZodString
的实例。如果要使用ZodString
的内置方法(例如.email()
),则必须在任何变换之前应用这些方法。
const emailToDomain = z
. string ( )
. email ( )
. transform ( ( val ) => val . split ( "@" ) [ 1 ] ) ;
emailToDomain . parse ( "[email protected]" ) ; // => example.com
The .transform
method can simultaneously validate and transform the value.这通常比链接transform
和完善更简单且refine
。
与.superRefine
一样,转换函数使用可用于注册验证问题的addIssue
方法接收ctx
对象。
const numberInString = z . string ( ) . transform ( ( val , ctx ) => {
const parsed = parseInt ( val ) ;
if ( isNaN ( parsed ) ) {
ctx . addIssue ( {
code : z . ZodIssueCode . custom ,
message : "Not a number" ,
} ) ;
// This is a special symbol you can use to
// return early from the transform function.
// It has type `never` so it does not affect the
// inferred return type.
return z . NEVER ;
}
return parsed ;
} ) ;
变换和改进可以交错。这些将按声明的顺序执行。
const nameToGreeting = z
. string ( )
. transform ( ( val ) => val . toUpperCase ( ) )
. refine ( ( val ) => val . length > 15 )
. transform ( ( val ) => `Hello ${ val } ` )
. refine ( ( val ) => val . indexOf ( "!" ) === - 1 ) ;
变换也可以是异步。
const IdToUser = z
. string ( )
. uuid ( )
. transform ( async ( id ) => {
return await getUserById ( id ) ;
} ) ;
配x 如果您的架构包含异步变换,则必须使用.parseasync()或.safeparseasync()来解析数据。否则ZOD将丢失错误。
.default
您可以使用变换来在ZOD中实现“默认值”的概念。
const stringWithDefault = z . string ( ) . default ( "tuna" ) ;
stringWithDefault . parse ( undefined ) ; // => "tuna"
可选地,您可以将函数传递到.default
中,每当需要生成默认值时,将重新执行:
const numberWithRandomDefault = z . number ( ) . default ( Math . random ) ;
numberWithRandomDefault . parse ( undefined ) ; // => 0.4413456736055323
numberWithRandomDefault . parse ( undefined ) ; // => 0.1871840107401901
numberWithRandomDefault . parse ( undefined ) ; // => 0.7223408162401552
从概念上讲,这就是ZOD处理默认值的方式:
undefined
,则返回默认值.describe
使用.describe()
将description
属性添加到生成的架构中。
const documentedString = z
. string ( )
. describe ( "A useful bit of text, if you know what to do with it." ) ;
documentedString . description ; // A useful bit of text…
这对于使用zod-to-json-schema
之类的库中的json架构中的字段来记录一个字段很有用。
.catch
使用.catch()
在解析错误时提供“捕获值”。
const numberWithCatch = z . number ( ) . catch ( 42 ) ;
numberWithCatch . parse ( 5 ) ; // => 5
numberWithCatch . parse ( "tuna" ) ; // => 42
Optionally, you can pass a function into .catch
that will be re-executed whenever a default value needs to be generated.包含捕获错误的ctx
对象将传递到此功能中。
const numberWithRandomCatch = z . number ( ) . catch ( ( ctx ) => {
ctx . error ; // the caught ZodError
return Math . random ( ) ;
} ) ;
numberWithRandomCatch . parse ( "sup" ) ; // => 0.4413456736055323
numberWithRandomCatch . parse ( "sup" ) ; // => 0.1871840107401901
numberWithRandomCatch . parse ( "sup" ) ; // => 0.7223408162401552
从概念上讲,这就是Zod处理“捕获值”的方式:
.optional
一种便利方法,它返回一个可选版本的模式。
const optionalString = z . string ( ) . optional ( ) ; // string | undefined
// equivalent to
z . optional ( z . string ( ) ) ;
.nullable
A convenience method that returns a nullable version of a schema.
const nullableString = z . string ( ) . nullable ( ) ; // string | null
// equivalent to
z . nullable ( z . string ( ) ) ;
.nullish
一种便利方法,它返回模式的“无效”版本。无效的模式将接受undefined
和null
。 Read more about the concept of "nullish" in the TypeScript 3.7 release notes.
const nullishString = z . string ( ) . nullish ( ) ; // string | null | undefined
// equivalent to
z . string ( ) . nullable ( ) . optional ( ) ;
.array
一种便利方法,该方法返回给定类型的数组模式:
const stringArray = z . string ( ) . array ( ) ; // string[]
// equivalent to
z . array ( z . string ( ) ) ;
.promise
承诺类型的便利方法:
const stringPromise = z . string ( ) . promise ( ) ; // Promise<string>
// equivalent to
z . promise ( z . string ( ) ) ;
.or
工会类型的便利方法。
const stringOrNumber = z . string ( ) . or ( z . number ( ) ) ; // string | number
// equivalent to
z . union ( [ z . string ( ) , z . number ( ) ] ) ;
.and
创建交叉类型的便利方法。
const nameAndAge = z
. object ( { name : z . string ( ) } )
. and ( z . object ( { age : z . number ( ) } ) ) ; // { name: string } & { age: number }
// equivalent to
z . intersection ( z . object ( { name : z . string ( ) } ) , z . object ( { age : z . number ( ) } ) ) ;
.brand
.brand<T>() => ZodBranded<this, B>
打字稿的类型系统是结构性的,这意味着在结构上等效的任何两种类型都被认为是相同的。
type Cat = { name : string } ;
type Dog = { name : string } ;
const petCat = ( cat : Cat ) => { } ;
const fido : Dog = { name : "fido" } ;
petCat ( fido ) ; // works fine
在某些情况下,可以在打字条中模拟名义键入的内容。例如,您可能希望编写一个仅接受ZOD验证的输入的函数。这可以通过品牌类型(又称不透明类型)来实现。
const Cat = z . object ( { name : z . string ( ) } ) . brand < "Cat" > ( ) ;
type Cat = z . infer < typeof Cat > ;
const petCat = ( cat : Cat ) => { } ;
// this works
const simba = Cat . parse ( { name : "simba" } ) ;
petCat ( simba ) ;
// this doesn't
petCat ( { name : "fido" } ) ;
在引擎盖下,这是通过使用相交类型将“品牌”附加到推理类型上的作用。 This way, plain/unbranded data structures are no longer assignable to the inferred type of the schema.
const Cat = z . object ( { name : z . string ( ) } ) . brand < "Cat" > ( ) ;
type Cat = z . infer < typeof Cat > ;
// {name: string} & {[symbol]: "Cat"}
请注意,品牌类型不会影响.parse
的运行时结果。这是一个仅静态构造。
.readonly
.readonly() => ZodReadonly<this>
此方法返回一个ZodReadonly
Schema实例,该实例使用基本架构解析输入,然后在结果上调用Object.freeze()
。推断的类型也标记为readonly
。
const schema = z . object ( { name : z . string ( ) } ) . readonly ( ) ;
type schema = z . infer < typeof schema > ;
// Readonly<{name: string}>
const result = schema . parse ( { name : "fido" } ) ;
result . name = "simba" ; // error
相关时,推断的类型使用Typescript的内置可读类型。
z . array ( z . string ( ) ) . readonly ( ) ;
// readonly string[]
z . tuple ( [ z . string ( ) , z . number ( ) ] ) . readonly ( ) ;
// readonly [string, number]
z . map ( z . string ( ) , z . date ( ) ) . readonly ( ) ;
// ReadonlyMap<string, Date>
z . set ( z . string ( ) ) . readonly ( ) ;
// ReadonlySet<string>
.pipe
模式可以链接到验证“管道”中。 It's useful for easily validating the result after a .transform()
:
z . string ( )
. transform ( ( val ) => val . length )
. pipe ( z . number ( ) . min ( 5 ) ) ;
.pipe()
方法返回ZodPipeline
实例。
.pipe()
to fix common issues with z.coerce
.您可以将输入限制为与所选强制良好合作的类型。然后使用.pipe()
应用胁迫。
without constrained input:
const toDate = z . coerce . date ( ) ;
// works intuitively
console . log ( toDate . safeParse ( "2023-01-01" ) . success ) ; // true
// might not be what you want
console . log ( toDate . safeParse ( null ) . success ) ; // true
输入约束:
const datelike = z . union ( [ z . number ( ) , z . string ( ) , z . date ( ) ] ) ;
const datelikeToDate = datelike . pipe ( z . coerce . date ( ) ) ;
// still works intuitively
console . log ( datelikeToDate . safeParse ( "2023-01-01" ) . success ) ; // true
// more likely what you want
console . log ( datelikeToDate . safeParse ( null ) . success ) ; // false
您还可以使用此技术来避免施加无人错误的胁迫。
without constrained input:
const toBigInt = z . coerce . bigint ( ) ;
// works intuitively
console . log ( toBigInt . safeParse ( "42" ) ) ; // true
// probably not what you want
console . log ( toBigInt . safeParse ( null ) ) ; // throws uncaught error
输入约束:
const toNumber = z . number ( ) . or ( z . string ( ) ) . pipe ( z . coerce . number ( ) ) ;
const toBigInt = z . bigint ( ) . or ( toNumber ) . pipe ( z . coerce . bigint ( ) ) ;
// still works intuitively
console . log ( toBigInt . safeParse ( "42" ) . success ) ; // true
// error handled by zod, more likely what you want
console . log ( toBigInt . safeParse ( null ) . success ) ; // false
You can extract the TypeScript type of any schema with z.infer<typeof mySchema>
.
const A = z . string ( ) ;
type A = z . infer < typeof A > ; // string
const u : A = 12 ; // TypeError
const u : A = "asdf" ; // compiles
变换呢?
In reality each Zod schema internally tracks two types: an input and an output.对于大多数模式(例如z.string()
),这两个是相同的。但是,一旦添加到混合物中,这两个值就会差异。例如z.string().transform(val => val.length)
具有string
的输入和number
输出。
您可以单独提取输入类型和输出类型:
const stringToNumber = z . string ( ) . transform ( ( val ) => val . length ) ;
// ️ Important: z.infer returns the OUTPUT type!
type input = z . input < typeof stringToNumber > ; // string
type output = z . output < typeof stringToNumber > ; // number
// equivalent to z.output!
type inferred = z . infer < typeof stringToNumber > ; // number
使用TypeScript Generics,您可以编写可重复使用的功能,以接受ZOD模式为参数。这使您能够创建自定义验证逻辑,模式转换等等,同时保持类型的安全性和推理。
当尝试编写接受ZOD模式作为输入的函数时,尝试这样的事情很容易:
function inferSchema < T > ( schema : z . ZodType < T > ) {
return schema ;
}
这种方法是不正确的,并且限制了打字稿正确推断论点的能力。无论您传递什么, schema
的类型都将是ZodType
的实例。
inferSchema ( z . string ( ) ) ;
// => ZodType<string>
This approach loses type information, namely which subclass the input actually is (in this case, ZodString
).这意味着您无法在inferSchema
结果上调用任何特定于.min()
的方法。
一种更好的方法是推断整个模式,而不仅仅是其推断类型。 You can do this with a utility type called z.ZodTypeAny
.
function inferSchema < T extends z . ZodTypeAny > ( schema : T ) {
return schema ;
}
inferSchema ( z . string ( ) ) ;
// => ZodString
ZodTypeAny
is just a shorthand forZodType<any, any, any>
, a type that is broad enough to match any Zod schema.
The Result is now fully and properly typed, and the type system can infer the specific subclass of the schema.
If you follow the best practice of using z.ZodTypeAny
as the generic parameter for your schema, you may encounter issues with the parsed data being typed as any
instead of the inferred type of the schema.
function parseData < T extends z . ZodTypeAny > ( data : unknown , schema : T ) {
return schema . parse ( data ) ;
}
parseData ( "sup" , z . string ( ) ) ;
// => any
Due to how TypeScript inference works, it is treating schema
like a ZodTypeAny
instead of the inferred type. You can fix this with a type cast using z.infer
.
function parseData < T extends z . ZodTypeAny > ( data : unknown , schema : T ) {
return schema . parse ( data ) as z . infer < T > ;
// ^^^^^^^^^^^^^^ <- add this
}
parseData ( "sup" , z . string ( ) ) ;
// => string
The ZodType
class has three generic parameters.
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:
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>'
Zod provides a subclass of Error called ZodError
. ZodErrors contain an issues
array containing detailed information about the validation problems.
const result = z
. object ( {
name : z . string ( ) ,
} )
. safeParse ( { name : 12 } ) ;
if ( ! result . success ) {
result . error . issues ;
/* [
{
"code": "invalid_type",
"expected": "string",
"received": "number",
"path": [ "name" ],
"message": "Expected string, received number"
}
] */
}
For detailed information about the possible error codes and how to customize error messages, check out the dedicated error handling guide: ERROR_HANDLING.md
Zod's error reporting emphasizes completeness and correctness . If you are looking to present a useful error message to the end user, you should either override Zod's error messages using an error map (described in detail in the Error Handling guide) or use a third-party library like zod-validation-error
You can use the .format()
method to convert this error into a nested object.
const result = z
. object ( {
name : z . string ( ) ,
} )
. safeParse ( { name : 12 } ) ;
if ( ! result . success ) {
const formatted = result . error . format ( ) ;
/* {
name: { _errors: [ 'Expected string, received number' ] }
} */
formatted . name ?. _errors ;
// => ["Expected string, received number"]
}
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.
https://github.com/hapijs/joi
Doesn't support static type inference ?
https://github.com/jquense/yup
Yup is a full-featured library that was implemented first in vanilla JS, and later rewritten in TypeScript.
https://github.com/gcanti/io-ts
io-ts is an excellent library by gcanti. The API of io-ts heavily inspired the design of Zod.
In our experience, io-ts prioritizes functional programming purity over developer experience in many cases. This is a valid and admirable design goal, but it makes io-ts particularly hard to integrate into an existing codebase with a more procedural or object-oriented bias. For instance, consider how to define an object with optional properties in io-ts:
import * as t from "io-ts" ;
const A = t . type ( {
foo : t . string ,
} ) ;
const B = t . partial ( {
bar : t . number ,
} ) ;
const C = t . intersection ( [ A , B ] ) ;
type C = t . TypeOf < typeof C > ;
// returns { foo: string; bar?: number | undefined }
You must define the required and optional props in separate object validators, pass the optionals through t.partial
(which marks all properties as optional), then combine them with t.intersection
.
Consider the equivalent in Zod:
const C = z . object ( {
foo : z . string ( ) ,
bar : z . number ( ) . optional ( ) ,
} ) ;
type C = z . infer < typeof C > ;
// returns { foo: string; bar?: number | undefined }
This more declarative API makes schema definitions vastly more concise.
io-ts
also requires the use of gcanti's functional programming library fp-ts
to parse results and handle errors. This is another fantastic resource for developers looking to keep their codebase strictly functional. But depending on fp-ts
necessarily comes with a lot of intellectual overhead; a developer has to be familiar with functional programming concepts and the fp-ts
nomenclature to use the library.
fp-ts
compatibility[T, ...T[]]
)https://github.com/pelotom/runtypes
Good type inference support.
[T, ...T[]]
)https://github.com/sindresorhus/ow
Ow is focused on function input validation. It's a library that makes it easy to express complicated assert statements, but it doesn't let you parse untyped data. They support a much wider variety of types; Zod has a nearly one-to-one mapping with TypeScript's type system, whereas ow lets you validate several highly-specific types out of the box (eg int32Array
, see full list in their README).
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.
View the changelog at CHANGELOG.md