https://zod.dev
使用靜態類型推理的打字稿優先架構驗證
這些文檔已被翻譯成中文。
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錢包 | Jason Lengstorf | 全球照明公司 |
主民 | 瑞安·帕爾默(Ryan Palmer) | 邁克爾·斯威尼 | NextBase |
依據 | 康納·辛諾特(Connor Sinnott) | Mohammad-Alia'râbi | supatool |
越來越多的工具在本地建立或支持ZOD!如果您在ZOD之上構建了工具或庫,請在Twitter上告訴我有關它的信息或開始討論。我將在下面添加並發推文。
tRPC
:構建無GraphQl的端到端TypeAfe API。@anatine/zod-nestjs
:用於在Nestjs項目中使用ZOD的幫助方法。zod-endpoints
:與ZOD的合同最嚴格鍵入端點。 OpenAPI兼容。zhttp
:一個具有ZOD輸入和響應驗證的OpenAPI兼容,嚴格鍵入的HTTP庫。domain-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
:Zod的社區維護的Formik適配器。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
:用於支持ZOD驗證的SolidJ的模塊化表單庫。houseform
:使用ZOD進行驗證的React表單庫。sveltekit-superforms
:帶有ZOD驗證的Sveltekit的增壓表單庫。mobx-zod-form
:基於MOBX&ZOD的數據優先形式構建器。@vee-validate/zod
:帶有ZOD架構驗證的vue.js庫。zod-form-renderer
:從ZOD模式的自動侵入表格,並使用E2E型安全性搭配鉤形式。 zod-to-ts
:從ZOD模式生成打字稿定義。zod-to-json-schema
:將您的ZOD模式轉換為JSON模式。@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
:用於架構驗證的通用適配器。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
:從您的Prisma模式中排放ZOD模式。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
:從OpenAPI模式中生成SDK和ZOD模式@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成本優化器。slonik
:Node.js Postgres客戶端具有強大的ZOD集成。schemql
:通過將RAW SQL與目標類型安全性和架構驗證相結合來增強您的SQL工作流程。soly
:使用ZOD創建CLI應用程序。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模式的受控環境。現場演示。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現在提供了一種更方便的脅迫原始價值的方法。
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"
返回的架構是一個普通的ZodString
實例,因此您可以使用所有字符串方法。
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包含少數特定於字符串的驗證。
// 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 ( ) ;
請查看驗證器,以獲取許多其他有用的字符串驗證功能,可以與改進結合使用。
創建字符串架構時,您可以自定義一些常見的錯誤消息。
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" } ) ;
您可能已經註意到,ZOD字符串包括一些與日期/時間相關的驗證。這些驗證是基於正則表達式的,因此它們不像全日期/時間庫那樣嚴格。但是,它們非常方便驗證用戶輸入。
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
在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
是一種用固定的允許字符串值組聲明架構的ZOD本地方式。將值的數組直接傳遞到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",
}
*/
您還可以將選項列表檢索為帶有.options
屬性的元組:
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枚舉是定義和驗證枚舉的建議方法。但是,如果您需要對第三方庫中的枚舉進行驗證(或者您不想重寫現有的枚舉),則可以使用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 };
您可以使用.unwrap()
從ZodOptional
實例中提取包裹的架構。
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
方法很淺 - 它僅適用一個深度。還有一個“深”版本:
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
默認情況下,ZOD對象架構在解析過程中剝離未識別的鍵。
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對象架構在解析過程中剝離未識別的鍵。您可以禁止使用.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
使用.element
訪問數組元素的架構。
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
關於數值鍵的註釋
儘管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
感謝Crasite的這個示例。
當使用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 ( ) ) ,
} ) ;
感謝Marcus13371337和Joelbeeldi此示例。
如果要驗證任何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
將附加驗證步驟附加到現有承諾上。您必須在返回的承諾上使用.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正確推斷出返回函數的類型。 (實際上返回功能實際上返回未定義。)
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文檔。)
但是有時您想在解析發生之前對輸入進行一些轉換。常見用例:類型強制。 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
您可以通過傳遞第二個參數來自定義錯誤消息和其他選項。此參數的工作方式與.refine
的參數相同。
z . custom < ... > ( ( val ) => ... , "custom error message" ) ;
所有ZOD模式都包含某些方法。
.parse
.parse(data: unknown): T
給定任何ZOD模式,您可以調用其.parse
方法檢查data
是否有效。如果是這樣,則返回一個帶有完整類型信息的值!否則,會丟棄錯誤。
重要的是:
.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可讓您通過改進提供自定義驗證邏輯。 (有關創建多個問題和自定義錯誤代碼等高級功能,請參見.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
。任何真實價值都將通過驗證。 (在[email protected]之前,驗證功能必須返回布爾值。) 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
方法來解析數據!否則ZOD將丟失錯誤。
變換和改進可以交錯:
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。
默認情況下,即使在完善檢查失敗後,解析也將繼續。例如,如果您將多個修補鍊鍊在一起,則將全部執行。但是,希望儘早中止以防止後來的細化被執行。為了實現這一目標,請將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" ,
} ) ;
}
} ) ;
如果您為.refine()
或.superRefine()
提供類型的謂詞,則將結果的類型範圍縮小到您的謂詞類型。如果您要混合多個鍊式的改進和轉換,這將很有用:
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
.transform
方法可以同時驗證和轉換值。這通常比鏈接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
可選地,您可以將函數傳遞到.catch
中,每當需要生成默認值時,該函數將被重新執行。包含捕獲錯誤的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
一種便利方法,它返回一個無效的模式版本。
const nullableString = z . string ( ) . nullable ( ) ; // string | null
// equivalent to
z . nullable ( z . string ( ) ) ;
.nullish
一種便利方法,它返回模式的“無效”版本。無效的模式將接受undefined
和null
。在打字稿3.7發行說明中閱讀有關“無效”概念的更多信息。
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" } ) ;
在引擎蓋下,這是通過使用相交類型將“品牌”附加到推理類型上的作用。這樣,普通/未品牌的數據結構不再可分配給架構的推斷類型。
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
模式可以鏈接到驗證“管道”中。在.transform()
:
z . string ( )
. transform ( ( val ) => val . length )
. pipe ( z . number ( ) . min ( 5 ) ) ;
.pipe()
方法返回ZodPipeline
實例。
.pipe()
來解決z.coerce
的常見問題。您可以將輸入限制為與所選強制良好合作的類型。然後使用.pipe()
應用脅迫。
沒有約束的輸入:
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
您還可以使用此技術來避免施加無人錯誤的脅迫。
沒有約束的輸入:
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
您可以使用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
變換呢?
實際上,每個ZOD架構內部都會跟踪兩種類型:輸入和輸出。對於大多數模式(例如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>
此方法失去類型信息,即輸入實際是哪個子類(在這種情況下為ZodString
)。這意味著您無法在inferSchema
結果上調用任何特定於.min()
的方法。
一種更好的方法是推斷整個模式,而不僅僅是其推斷類型。您可以使用稱為z.ZodTypeAny
的實用程序類型來執行此操作。
function inferSchema < T extends z . ZodTypeAny > ( schema : T ) {
return schema ;
}
inferSchema ( z . string ( ) ) ;
// => ZodString
ZodTypeAny
只是ZodType<any, any, any>
,一種足夠寬的類型以匹配任何Zod模式的類型。
結果現在已完全且正確地鍵入,並且類型系統可以推斷該模式的特定子類。
如果您遵循使用z.ZodTypeAny
作為模式的通用參數的最佳實踐,則可能會遇到問題的問題,而解析的數據被鍵入any
而不是推斷的模式類型。
function parseData < T extends z . ZodTypeAny > ( data : unknown , schema : T ) {
return schema . parse ( data ) ;
}
parseData ( "sup" , z . string ( ) ) ;
// => any
由於Typescript推斷的工作原理,它將schema
像ZodTypeAny
而不是推斷類型一樣。您可以使用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
ZodType
類具有三個通用參數。
class ZodType <
Output = any ,
Def extends ZodTypeDef = ZodTypeDef ,
Input = Output
> { ... }
通過將這些限制在通用輸入中,您可以限制允許允許的模式作為輸入到您的函數:
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提供了一個稱為ZodError
的錯誤子類。 Zoderrors包含一個issues
,其中包含有關驗證問題的詳細信息。
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"
}
] */
}
有關可能的錯誤代碼以及如何自定義錯誤消息的詳細信息,請查看專用錯誤處理指南:error_handling.md
ZOD的錯誤報告強調完整性和正確性。如果您想向最終用戶提供有用的錯誤消息,則應使用錯誤映射覆蓋ZOD的錯誤消息(在錯誤處理指南中詳細描述)或使用第三方庫(如zod-validation-error
您可以使用.format()
方法將此錯誤轉換為嵌套對象。
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"]
}
還有少數其他廣泛使用的驗證庫,但是所有這些庫都有某些設計限制,可以為非理想的開發人員體驗。
https://github.com/hapijs/joi
不支持靜態類型推理?
https://github.com/jquense/yup
YUP是一個功能齊全的庫,首先在Vanilla JS中實現,然後在打字稿中重寫。
https://github.com/gcanti/io-ts
IO-TS是GCANTI的出色圖書館。 IO-TS的API極大地啟發了Zod的設計。
根據我們的經驗,在許多情況下,IO-TS優先考慮功能編程純度而不是開發人員的經驗。這是一個有效且令人欽佩的設計目標,但它使IO-TS特別難以集成到具有更具程序性或面向對象的偏差的現有代碼庫中。例如,考慮如何在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 }
您必須在單獨的對象驗證器中定義所需的和可選的道具,通過t.partial
(將所有屬性標記為可選)傳遞選項,然後將它們與t.intersection
結合使用。
考慮ZOD中的等效物:
const C = z . object ( {
foo : z . string ( ) ,
bar : z . number ( ) . optional ( ) ,
} ) ;
type C = z . infer < typeof C > ;
// returns { foo: string; bar?: number | undefined }
這種聲明性的API使模式定義更加簡潔。
io-ts
還需要使用GCANTI的功能編程庫fp-ts
來解析結果並處理錯誤。對於希望保持其代碼庫嚴格功能的開發人員來說,這是另一個絕妙的資源。但是,根據fp-ts
,一定有很多智力開銷。開發人員必須熟悉功能編程概念和使用庫的fp-ts
命名法。
fp-ts
兼容性[T, ...T[]]
)https://github.com/pelotom/runtypes
良好的類型推理支持。
[T, ...T[]]
)https://github.com/sindresorhus/ow
OW專注於功能輸入驗證。這是一個庫,可以輕鬆表達複雜的斷言語句,但它不能讓您解析非類型的數據。他們支持多種多樣的類型。 Zod具有與打字稿類型系統的近一對一對一映射,而OW可以驗證包裝盒的幾種高度特定的類型(例如, int32Array
,請參見其ReadMe中的完整列表)。
如果要驗證函數輸入,請在zod中使用函數模式!這是一種簡單得多的方法,可讓您重複使用函數類型聲明而無需重複自己(即,在每個函數的開頭,抄寫了一堆OW斷言)。 ZOD還可以驗證返回類型,因此您可以確保不會在下游傳遞任何意外數據。
在ChangElog.md上查看ChangElog