https://zod.dev
TypeScript-First Schema Validation avec une inférence de type statique
Ces documents ont été traduits en chinois.
npm
(nœud / bun)deno.land/x
(Deno).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()
to fix common issues with z.coerce
. ZOD est une bibliothèque de schéma et de validation de schéma TypeScript-First. J'utilise le terme "schéma" pour me référer largement à tout type de données, d'une simple string
à un objet imbriqué complexe.
Zod est conçu pour être aussi adapté aux développeurs que possible. L'objectif est d'éliminer les déclarations de type duplication. Avec ZOD, vous déclarez un validateur une fois et ZOD déduire automatiquement le type de dactylographie statique. Il est facile de composer des types plus simples dans des structures de données complexes.
Quelques autres grands aspects:
.optional()
) Renvoie une nouvelle instanceLe parrainage à n'importe quel niveau est apprécié et encouragé. Si vous avez construit un produit payant à l'aide de ZOD, considérez l'un des niveaux d'entreprise.
La plate-forme de gestion des utilisateurs la plus complète
commis.com
|
|
|
|
|
Propulanth | Cerbos | Scalaire | Trigger.dev |
Transloadit | Infiaté | Whop | Cryptojobslist |
Plaine. | Ininterrompu | Blok | Mux |
Brandon Bayer | Jiří Brabec | Alex Johansson | Systèmes fongibles |
Adaptable | Portefeuille avana | Jason Lengstorf | Global Illumination, Inc. |
Mandatorn | Ryan Palmer | Michael Sweeney | Base suivante |
Rémotion | Connor Sinnott | Mohammad-ali a'râbi | Supatool |
Il existe un nombre croissant d'outils qui sont construits au sommet ou prennent en charge Zod nativement! Si vous avez construit un outil ou une bibliothèque au-dessus de Zod, parlez-moi sur Twitter ou commencez une discussion. Je vais l'ajouter ci-dessous et le tweeter.
tRPC
: Créez des API de type de bout en bout sans graphQL.@anatine/zod-nestjs
: méthodes d'aide à l'utilisation de ZOD dans un projet NESTJS.zod-endpoints
: Contrat First Stricly Typed Points avec ZOD. OpenAPI compatible.zhttp
: une bibliothèque HTTP compatible OpenAPI, strictement typique avec entrée et validation de réponse ZOD.domain-functions
: Découpez votre logique commerciale à partir de votre framework à l'aide de fonctions composables. Avec une inférence de type de première classe de bout en bout alimenté par des schémas zod.@zodios/core
: un client API TypeScript avec validation du temps d'exécution et de compilation soutenue par Axios et Zod.express-zod-api
: Build Express-basés sur des API avec validation du schéma d'E / S et MiddleWares personnalisés.tapiduck
: API JSON TypeSafe de bout en bout avec ZOD et Express; Un peu comme TRPC, mais plus simple.koa-zod-router
: Créez des routes TypeSafe en KOA avec validation d'E / S à l'aide de ZOD.zod-sockets
: Zod-powered Socket.IO microframework with I/O validation and built-in AsyncAPI specs react-hook-form
: Un résolveur ZOD à partie pour le formulaire React Hook.zod-validation-error
: générer des messages d'erreur conviviaux de ZodError
s.zod-formik-adapter
: un adaptateur Formik entretenu dans la communauté pour Zod.react-zorm
: Standalone <form>
generation and validation for React using Zod.zodix
: Utilitaires Zod pour FormData et UrlSearchParams dans les chargeurs et les actions de remix.conform
: une bibliothèque de validation de formulaire de typeSafe pour l'amélioration progressive des formulaires HTML. Works with Remix and Next.js.remix-params-helper
: Simplifiez l'intégration de ZOD avec UrlSearchParams standard et FormData pour les applications Remix.formik-validator-zod
: Bibliothèque de validateurs conformes à FormIK qui simplifie en utilisant ZOD avec Formik.zod-i18n-map
: utile pour traduire les messages d'erreur ZOD.@modular-forms/solid
: bibliothèque de formulaires modulaires pour SolidJS qui prend en charge ZOD pour la validation.houseform
: une bibliothèque de formulaires React qui utilise ZOD pour la validation.sveltekit-superforms
: Bibliothèque de formulaires suralimentée pour Sveltekit avec validation ZOD.mobx-zod-form
: Data First Form Builder basé sur MOBX & ZOD.@vee-validate/zod
: Bibliothèque de formulaires pour Vue.js avec validation du schéma ZOD.zod-form-renderer
: champs de formulaire automatique du schéma ZOD et les rendre avec une forme réactive-forage avec la sécurité de type E2E. zod-to-ts
: générer des définitions de typeScript à partir de schémas zod.zod-to-json-schema
: Convertissez vos schémas ZOD en schémas JSON.@anatine/zod-openapi
: convertit un schéma zod en un SchemaObject
OpenAPI v3.x.zod-fast-check
: Générez des arbitraires fast-check
à partir de schémas ZOD.zod-dto
: Générez Nest.js DTOS à partir d'un schéma ZOD.fastify-type-provider-zod
: Créez des fournisseurs de type Fastify à partir de schémas ZOD.zod-to-openapi
: Générez des documents OpenAPI complets (Swagger) de ZOD, y compris les schémas, les points de terminaison et les paramètres.nestjs-graphql-zod
: génère des classes de modèle GraphQL NESTJS à partir de schémas ZOD. Fournit des décorateurs de méthode GraphQL travaillant avec des schémas ZOD.zod-openapi
: Créez une documentation complète OpenAPI V3.x à partir de schémas ZOD.fastify-zod-openapi
: Fastify Type Provider, Validation, Serialization et @ Fastify / Swagger Pourince pour les schémas ZOD.typeschema
: adaptateur universel pour la validation du schéma.zodex
: (DE) sérialisation pour les schémas zod ts-to-zod
: Convertir les définitions de typeScript en schémas zod.@runtyping/zod
: Générez Zod à partir de types statiques et de schéma JSON.json-schema-to-zod
: Convertissez vos schémas JSON en schémas zod. Démo en direct.json-to-zod
: Convertir les objets JSON en schémas ZOD. Démo en direct.graphql-codegen-typescript-validation-schema
: GraphQL Code Generator Plugin pour générer un schéma de validation de formulaire à partir de votre schéma GraphQL.zod-prisma
: Générez des schémas zod à partir de votre schéma PRISMA.Supervillain
: Générez des schémas ZOD à partir de vos structures Go.prisma-zod-generator
: émettez des schémas zod de votre schéma PRISMA.drizzle-zod
: émettez des schémas zod de votre schéma de bruine.prisma-trpc-generator
: émettez entièrement les routeurs TRPC et leurs schémas de validation à l'aide de ZOD.zod-prisma-types
créent des types zod à partir de vos modèles PRISMA.quicktype
: Convertir des objets JSON et des schémas JSON en schémas zod.@sanity-typed/zod
: Générez des schémas zod à partir de schémas de santé mentale.java-to-zod
: convertir les pojos en schémas zodOrval
: Generate Zod schemas from OpenAPI schemasKubb
: Générez des schémas SDK et ZOD de vos schémas OpenAPI @anatine/zod-mock
: Générez des données simulées à partir d'un schéma ZOD. Powered by faker.js.zod-mocking
: Générez des données simulées à partir de vos schémas ZOD.zod-fixture
: Utilisez vos schémas ZOD pour automatiser la génération de luminaires de test non pertinents de manière déterministe.zocker
: Generate plausible mock-data from your schemas.zodock
génère des données simulées basées sur les schémas ZOD.zod-schema-faker
génère des données simulées à partir de schémas ZOD. Propulsé par @ faker-js / faker et randexp.js freerstore
: Firestore Cost Optimizer.slonik
: Node.js Postgres Client avec une forte intégration ZOD.schemql
: améliore votre flux de travail SQL en combinant SQL brut avec la sécurité et la validation du schéma ciblées.soly
: Créer des applications CLI avec ZOD.pastel
: Créez des applications CLI avec React, Zod et Ink.zod-xlsx
: un validateur de ressources basé sur XLSX utilisant des schémas ZOD.znv
: analyse et validation d'environnement de type type pour Node.js avec des schémas ZOD.zod-config
: Chargez des configurations sur plusieurs sources avec des adaptateurs flexibles, assurant la sécurité du type avec ZOD.unplugin-environment
: un plugin pour charger des variables d'environnement en toute sécurité avec la validation du schéma, simple avec un module virtuel, un type de type avec Intellisense, et un meilleur dx ?. Propulsé par Zod. zod_utilz
: Utilitaires agnostiques du cadre pour ZOD.zod-playground
: un outil pour apprendre et tester les fonctionnalités de validation du schéma ZOD. Lien.zod-sandbox
: Environnement contrôlé pour tester les schémas ZOD. Démo en direct.zod-dev
: Désactive conditionnellement l'analyse de runtime ZOD en production.zod-accelerator
: accélère le débit de Zod jusqu'à ~ 100x. TypeScript 4.5+!
Vous devez activer le mode strict
dans votre tsconfig.json
. C'est une meilleure pratique pour tous les projets de typeScript.
// tsconfig.json
{
// ...
"compilerOptions" : {
// ...
"strict" : true
}
}
npm
(nœud / bun) npm install zod # npm
yarn add zod # yarn
bun add zod # bun
pnpm add zod # pnpm
Zod publie également une version Canari sur chaque validation. Pour installer le Canary:
npm install zod@canary # npm
yarn add zod@canary # yarn
bun add zod@canary # bun
pnpm add zod@canary # pnpm
deno.land/x
(Deno)Contrairement au nœud, Deno s'appuie sur les importations directes d'URL au lieu d'un gestionnaire de packages comme NPM. Zod est disponible sur Deno.land/X. La dernière version peut être importée comme:
import { z } from "https://deno.land/x/zod/mod.ts" ;
Vous pouvez également spécifier une version particulière:
import { z } from "https://deno.land/x/[email protected]/mod.ts" ;
Le reste de ce ReadMe suppose que vous utilisez NPM et que vous importez directement du package
"zod"
.
Création d'un schéma de cordes simple
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 }
Creating an object schema
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"
Pendant l'étape d'analyse, l'entrée est transmise par la fonction String()
, qui est un JavaScript intégré pour contraindre les données dans les chaînes.
schema . parse ( 12 ) ; // => "12"
schema . parse ( true ) ; // => "true"
schema . parse ( undefined ) ; // => "undefined"
schema . parse ( null ) ; // => "null"
Le schéma retourné est une instance ZodString
normale, vous pouvez donc utiliser toutes les méthodes de chaîne.
z . coerce . string ( ) . email ( ) . min ( 5 ) ;
Comment fonctionne la coercition
All primitive types support coercion. ZOD contraint toutes les entrées à l'aide des constructeurs intégrés: String(input)
, Number(input)
, new Date(input)
, etc.
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)
Remarque - La coercition booléenne avec z.coerce.boolean()
peut ne pas fonctionner comme vous vous y attendez. Any truthy value is coerced to true
, and any falsy value is coerced to 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
Pour plus de contrôle sur la logique de coercition, envisagez d'utiliser z.preprocess
ou z.pipe()
.
Les schémas littéraux représentent un type littéral, comme "hello world"
ou 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"
Actuellement, il n'y a pas de soutien aux littéraux de date dans Zod. Si vous avez un cas d'utilisation pour cette fonctionnalité, veuillez déposer un problème.
ZOD comprend une poignée de validations spécifiques à la chaîne.
// 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 ( ) ;
Consultez Validator.js pour un tas d'autres fonctions de validation de chaîne utiles qui peuvent être utilisées en conjonction avec des raffinements.
Vous pouvez personnaliser certains messages d'erreur courants lors de la création d'un schéma de chaîne.
const name = z . string ( {
required_error : "Name is required" ,
invalid_type_error : "Name must be a string" ,
} ) ;
Lorsque vous utilisez des méthodes de validation, vous pouvez passer dans un argument supplémentaire pour fournir un message d'erreur personnalisé.
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" } ) ;
Comme vous l'avez peut-être remarqué, Zod String comprend quelques validations liées à la date / heure. Ces validations sont basées sur une expression régulière, elles ne sont donc pas aussi strictes qu'une bibliothèque de date / heure complète. Cependant, ils sont très pratiques pour valider l'entrée des utilisateurs.
La méthode z.string().datetime()
applique ISO 8601; La valeur par défaut n'est pas des décalages de fuseau horaire et une précision décimale de sous-seconde arbitraire.
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)
Les décalages du fuseau horaire peuvent être autorisés en définissant l'option offset
sur 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)
Vous pouvez également limiter la precision
admissible. Par défaut, la précision de sous-seconde arbitraire est prise en charge (mais facultative).
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
Ajouté dans ZOD 3.23
La méthode z.string().date()
valide les chaînes au format 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
Ajouté dans ZOD 3.23
La méthode z.string().time()
valide les chaînes au format HH:MM:SS[.s+]
. Le second peut inclure une précision décimale arbitraire. Il n'autorise les décalages du fuseau horaire d'aucune sorte.
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)
Vous pouvez définir l'option precision
pour contraindre la précision décimale admissible.
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
La méthode z.string().ip()
valide par défaut IPv4 et 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
Vous pouvez également définir la version
IP.
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
Vous pouvez personnaliser certains messages d'erreur lors de la création d'un schéma de nombre.
const age = z . number ( {
required_error : "Age is required" ,
invalid_type_error : "Age must be a number" ,
} ) ;
ZOD comprend une poignée de validations spécifiques au nombre.
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
Facultativement, vous pouvez passer dans un deuxième argument pour fournir un message d'erreur personnalisé.
z . number ( ) . lte ( 5 , { message : "this?is?too?big" } ) ;
Zod comprend une poignée de validations spécifiques à 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.
Vous pouvez personnaliser certains messages d'erreur lors de la création d'un schéma NAN.
const isNaN = z . nan ( {
required_error : "isNaN is required" ,
invalid_type_error : "isNaN must be 'not a number'" ,
} ) ;
Vous pouvez personnaliser certains messages d'erreur lors de la création d'un schéma booléen.
const isActive = z . boolean ( {
required_error : "isActive is required" ,
invalid_type_error : "isActive must be a boolean" ,
} ) ;
Utilisez z.date () pour valider les instances Date
.
z . date ( ) . safeParse ( new Date ( ) ) ; // success: true
z . date ( ) . safeParse ( "2022-01-12T00:00:00.000Z" ) ; // success: false
You can customize certain error messages when creating a date schema.
const myDateSchema = z . date ( {
required_error : "Please select a date and time" ,
invalid_type_error : "That's not a date!" ,
} ) ;
Zod fournit une poignée de validations spécifiques à la date.
z . date ( ) . min ( new Date ( "1900-01-01" ) , { message : "Too old" } ) ;
z . date ( ) . max ( new Date ( ) , { message : "Too young!" } ) ;
Coercition à ce jour
Since zod 3.20, use z.coerce.date()
to pass the input through 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
Pour les versions ZOD plus anciennes, utilisez z.preprocess
comme décrit dans ce fil.
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. Passez le tableau de valeurs directement dans z.enum()
. Alternativement, utilisez as const
pour définir vos valeurs d'énumération comme un tuple de cordes. See the const assertion docs for details.
const VALUES = [ "Salmon" , "Tuna" , "Trout" ] as const ;
const FishEnum = z . enum ( VALUES ) ;
Ce n'est pas autorisé, car Zod n'est pas en mesure de déduire les valeurs exactes de chaque élément.
const fish = [ "Salmon" , "Tuna" , "Trout" ] ;
const FishEnum = z . enum ( fish ) ;
.enum
Pour obtenir une assurance automatique avec une énumération Zod, utilisez la propriété .enum
de votre schéma:
FishEnum . enum . Salmon ; // => autocompletes
FishEnum . enum ;
/*
=> {
Salmon: "Salmon",
Tuna: "Tuna",
Trout: "Trout",
}
*/
Vous pouvez également récupérer la liste des options sous forme de tuple avec la propriété .options
:
FishEnum . options ; // ["Salmon", "Tuna", "Trout"];
.exclude/.extract()
Vous pouvez créer des sous-ensembles d'une énumération ZOD avec les méthodes .exclude
et .extract
.
const FishEnum = z . enum ( [ "Salmon" , "Tuna" , "Trout" ] ) ;
const SalmonAndTrout = FishEnum . extract ( [ "Salmon" , "Trout" ] ) ;
const TunaOnly = FishEnum . exclude ( [ "Salmon" , "Trout" ] ) ;
Les énumérations ZOD sont l'approche recommandée pour définir et valider les énumérations. But if you need to validate against an enum from a third-party library (or you don't want to rewrite your existing enums) you can use z.nativeEnum()
.
Énumériques numériques
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
Enus de cordes
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
Const Enum
La fonction .nativeEnum()
fonctionne également pour les objets as const
.as const
nécessite TypeScript 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
Vous pouvez accéder à l'objet sous-jacent avec la propriété .enum
:
FruitEnum . enum . Apple ; // "apple"
Vous pouvez faire n'importe quel schéma en option avec z.optional()
. Cela enveloppe le schéma dans une instance ZodOptional
et renvoie le résultat.
const schema = z . optional ( z . string ( ) ) ;
schema . parse ( undefined ) ; // => returns undefined
type A = z . infer < typeof schema > ; // string | undefined
Pour plus de commodité, vous pouvez également appeler la méthode .optional()
sur un schéma existant.
const user = z . object ( {
username : z . string ( ) . optional ( ) ,
} ) ;
type C = z . infer < typeof user > ; // { username?: string | undefined };
Vous pouvez extraire le schéma enveloppé à partir d'une instance ZodOptional
avec .unwrap()
.
const stringSchema = z . string ( ) ;
const optionalString = stringSchema . optional ( ) ;
optionalString . unwrap ( ) === stringSchema ; // true
De même, vous pouvez créer des types nullables avec z.nullable()
.
const nullableString = z . nullable ( z . string ( ) ) ;
nullableString . parse ( "asdf" ) ; // => "asdf"
nullableString . parse ( null ) ; // => null
Ou utilisez la méthode .nullable()
.
const E = z . string ( ) . nullable ( ) ; // equivalent to nullableString
type E = z . infer < typeof E > ; // string | null
Extraire le schéma intérieur avec .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
Utilisez .shape
pour accéder aux schémas pour une clé particulière.
Dog . shape . name ; // => string schema
Dog . shape . age ; // => number schema
.keyof
Utilisez .keyof
pour créer un schéma ZodEnum
à partir des clés d'un schéma d'objet.
const keySchema = Dog . keyof ( ) ;
keySchema ; // ZodEnum<["name", "age"]>
.extend
Vous pouvez ajouter des champs supplémentaires à un schéma d'objet avec la méthode .extend
.
const DogWithBreed = Dog . extend ( {
breed : z . string ( ) ,
} ) ;
Vous pouvez utiliser .extend
pour écraser les champs! Soyez prudent avec ce pouvoir!
.merge
Équivalent à 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 }
Si les deux schémas partagent des clés, les propriétés de B l'emporte sur la propriété de A. Le schéma retourné hérite également de la politique "UnknownKeys" (bande / strict / passhrough) et le schéma Catchall de B.
.pick/.omit
Inspirés par les types d'utilité de Pick
et Omit
de TypeScript, tous les schémas d'objets Zod ont des méthodes .pick
et .omit
qui renvoient une version modifiée. Considérez ce schéma de recette:
const Recipe = z . object ( {
id : z . string ( ) ,
name : z . string ( ) ,
ingredients : z . array ( z . string ( ) ) ,
} ) ;
Pour ne garder que certaines clés, utilisez .pick
.
const JustTheName = Recipe . pick ( { name : true } ) ;
type JustTheName = z . infer < typeof JustTheName > ;
// => { name: string }
Pour supprimer certaines clés, utilisez .omit
.
const NoIDRecipe = Recipe . omit ( { id : true } ) ;
type NoIDRecipe = z . infer < typeof NoIDRecipe > ;
// => { name: string, ingredients: string[] }
.partial
Inspirée par le type d'utilitaire .partial
intégré partiel, la méthode.
À partir de cet objet:
const user = z . object ( {
email : z . string ( ) ,
username : z . string ( ) ,
} ) ;
// { email: string; username: string }
Nous pouvons créer une version partielle:
const partialUser = user . partial ( ) ;
// { email?: string | undefined; username?: string | undefined }
Vous pouvez également spécifier quelles propriétés pour fabriquer facultatif:
const optionalEmail = user . partial ( {
email : true ,
} ) ;
/*
{
email?: string | undefined;
username: string
}
*/
.deepPartial
La méthode .partial
est peu profonde - elle applique un niveau de profondeur. 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}[]
}
*/
Limitation importante: les partiels profonds ne fonctionnent que comme prévu dans les hiérarchies des objets, des tableaux et des tuples.
.required
.required
à la .partial
.
Starting from this object:
const user = z
. object ( {
email : z . string ( ) ,
username : z . string ( ) ,
} )
. partial ( ) ;
// { email?: string | undefined; username?: string | undefined }
Nous pouvons créer une version requise:
const requiredUser = user . required ( ) ;
// { email: string; username: string }
Vous pouvez également spécifier les propriétés à réaliser:
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
Au lieu de cela, si vous souhaitez passer par des clés inconnues, utilisez .passthrough()
.
person . passthrough ( ) . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => { name: "bob dylan", extraKey: 61 }
.strict
Par défaut, les schémas d'objets ZOD suppriment les clés non reconnues pendant l'analyse. You can disallow unknown keys with .strict()
. S'il y a des touches inconnues dans l'entrée, Zod lancera une erreur.
const person = z
. object ( {
name : z . string ( ) ,
} )
. strict ( ) ;
person . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => throws ZodError
.strip
Vous pouvez utiliser la méthode .strip
pour réinitialiser un schéma d'objet au comportement par défaut (éliminer les touches non reconnues).
.catchall
Vous pouvez passer un schéma "Catchall" dans un schéma d'objet. Toutes les clés inconnues seront validées contre elle.
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
L'utilisation .catchall()
évite .passthrough()
, .strip()
, ou .strict()
. Toutes les clés sont maintenant considérées comme "connues".
const stringArray = z . array ( z . string ( ) ) ;
// equivalent
const stringArray = z . string ( ) . array ( ) ;
Soyez prudent avec la méthode .array()
. Il renvoie une nouvelle instance ZodArray
. Cela signifie que l' ordre dans lequel vous appelez des méthodes est importante. Par exemple:
z . string ( ) . optional ( ) . array ( ) ; // (string | undefined)[]
z . string ( ) . array ( ) . optional ( ) ; // string[] | undefined
.element
Utilisez .element
pour accéder au schéma pour un élément du tableau.
stringArray . element ; // => string schema
.nonempty
Si vous souhaitez vous assurer qu'un tableau contient au moins un élément, utilisez .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
Vous pouvez éventuellement spécifier un message d'erreur personnalisé:
// 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
Unlike .nonempty()
these methods do not change the inferred type.
Contrairement aux tableaux, les tuples ont un nombre fixe d'éléments et chaque élément peut avoir un type différent.
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 }]
Un argument variadique ("repos") peut être ajouté avec la méthode .rest
.
const variadicTuple = z . tuple ( [ z . string ( ) ] ) . rest ( z . number ( ) ) ;
const result = variadicTuple . parse ( [ "hello" , 1 , 2 , 3 ] ) ;
// => [string, ...number[]];
Zod comprend une méthode z.union
intégrée pour la composition "ou".
const stringOrNumber = z . union ( [ z . string ( ) , z . number ( ) ] ) ;
stringOrNumber . parse ( "foo" ) ; // passes
stringOrNumber . parse ( 14 ) ; // passes
ZOD testera l'entrée par rapport à chacune des "options" dans l'ordre et renverra la première valeur qui valide avec succès.
Pour plus de commodité, vous pouvez également utiliser la méthode .or
la méthode:
const stringOrNumber = z . string ( ) . or ( z . number ( ) ) ;
Validation de chaîne facultative:
Pour valider une entrée de formulaire facultative, vous pouvez unifier la validation de chaîne souhaitée avec un littéral de chaîne vide.
Cet exemple valide une entrée facultative mais doit contenir une URL valide:
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
Une union discriminée est une union de schémas d'objets qui partagent tous une clé particulière.
type MyUnion =
| { status : "success" ; data : string }
| { status : "failed" ; error : Error } ;
Ces syndicats peuvent être représentés avec la méthode z.discriminatedUnion
. Cela permet une évaluation plus rapide, car ZOD peut vérifier la clé de discriminatrice ( status
dans l'exemple ci-dessus) pour déterminer quel schéma doit être utilisé pour analyser l'entrée. Cela rend l'analyse plus efficace et permet à ZOD de signaler les erreurs plus conviviales.
Avec la méthode de l'Union de base, l'entrée est testée contre chacune des «options» fournies, et en cas d'invalidité, les problèmes pour toutes les «options» sont affichés dans l'erreur ZOD. D'un autre côté, le syndicat discriminé permet de sélectionner une seule des "options", de tester contre elle et de montrer uniquement les problèmes liés à cette "option".
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" } ) ;
Vous pouvez extraire une référence au tableau de schémas avec la propriété .options
.
myUnion . options ; // [ZodObject<...>, ZodObject<...>]
Pour fusionner deux ou plusieurs syndicats discriminés, utilisez .options
avec destructuration.
const A = z . discriminatedUnion ( "status" , [
/* options */
] ) ;
const B = z . discriminatedUnion ( "status" , [
/* options */
] ) ;
const AB = z . discriminatedUnion ( "status" , [ ... A . options , ... B . options ] ) ;
Les schémas d'enregistrement sont utilisés pour valider des types tels que Record<string, number>
. Ceci est particulièrement utile pour stocker ou mettre en cache des articles par 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 }>
Le schéma et le type inféré peuvent être utilisés comme tel:
const userStore : UserStore = { } ;
userStore [ "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd" ] = {
name : "Carlotta" ,
} ; // passes
userStore [ "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd" ] = {
whatever : "Ice cream sundae" ,
} ; // TypeError
Une note sur les clés numériques
Bien que z.record(keyType, valueType)
soit capable d'accepter les types de clés numériques et le type d'enregistrement intégré de TypeScript est Record<KeyType, ValueType>
, il est difficile de représenter le type de type Record<number, any>
dans zod.
Il s'avère que le comportement de TypeScript entourant [k: number]
est un peu inutile:
const testMap : { [ k : number ] : string } = {
1 : "one" ,
} ;
for ( const key in testMap ) {
console . log ( ` ${ key } : ${ typeof key } ` ) ;
}
// prints: `1: string`
As you can see, JavaScript automatically casts all object keys to strings under the hood. Étant donné que Zod essaie de combler l'écart entre les types statiques et les types d'exécution, il n'est pas logique de fournir un moyen de créer un schéma d'enregistrement avec des clés numériques, car il n'y a pas de clé numérique dans JavaScript d'exécution.
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>
Set schemas can be further constrained with the following utility methods.
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
Les intersections sont utiles pour créer des "types logiques et". This is useful for intersecting two object types.
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 ) ;
Bien que dans de nombreux cas, il est recommandé d'utiliser A.merge(B)
pour fusionner deux objets. La méthode .merge
renvoie une nouvelle instance ZodObject
, tandis que A.and(B)
renvoie une instance ZodIntersection
moins utile qui manque de méthodes d'objets communes comme 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
You can define a recursive schema in Zod, but because of a limitation of TypeScript, their type can't be statically inferred. Au lieu de cela, vous devrez définir manuellement la définition de type et le fournir à Zod comme un "indice de type".
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
Merci à Crasite pour cet exemple.
Lorsque vous utilisez z.ZodType
avec z.ZodEffects
( .refine
, .transform
, preprocess
, etc ...), vous devrez définir les types d'entrée et de sortie du schéma. 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 ( ) ) ,
} ) ;
Merci à Marcus13371337 et Joelbeeldi pour cet exemple.
Si vous souhaitez valider une valeur JSON, vous pouvez utiliser l'extrait ci-dessous.
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 ) ;
Merci à Ggoodman d'avoir suggéré cela.
Malgré le soutien des schémas récursifs, passer des données cycliques dans ZOD provoquera une boucle infinie dans certains cas.
Pour détecter les objets cycliques avant de causer des problèmes, considérez cette approche.
const numberPromise = z . promise ( z . number ( ) ) ;
"Parsing" fonctionne un peu différemment avec des schémas de promesses. La validation se produit en deux parties:
.then
et .catch
Méthodes..then
pour attacher une étape de validation supplémentaire sur la promesse existante. Vous devrez utiliser .catch
sur la promesse retournée de gérer les échecs de validation. 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
} ;
Vous pouvez utiliser z.instanceof
pour vérifier que l'entrée est une instance d'une classe. Ceci est utile pour valider les entrées contre les classes exportées à partir de bibliothèques tierces.
class Test {
name : string ;
}
const TestSchema = z . instanceof ( Test ) ;
const blob : any = "whatever" ;
TestSchema . parse ( new Test ( ) ) ; // passes
TestSchema . parse ( blob ) ; // throws
Zod vous permet également de définir des "schémas de fonction". Cela facilite la validation des entrées et des sorties d'une fonction sans mêler votre code de validation et "Logique métier".
Vous pouvez créer un schéma de fonction avec z.function(args, returnType)
.
const myFunction = z . function ( ) ;
type myFunction = z . infer < typeof myFunction > ;
// => ()=>unknown
Définissez les entrées et les sorties.
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
Les schémas de fonction ont une méthode .implement()
qui accepte une fonction et renvoie une nouvelle fonction qui valide automatiquement ses entrées et sorties.
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
Si vous ne vous souciez que de valider les entrées, n'appelez pas la méthode .returns()
. Le type de sortie sera déduit de la mise en œuvre.
Vous pouvez utiliser l'option spéciale
z.void()
si votre fonction ne renvoie rien. This will let Zod properly infer the type of void-returning functions. (Les fonctions de rendement du vide retournent en fait indéfinies.)
const myFunction = z
. function ( )
. args ( z . string ( ) )
. implement ( ( arg ) => {
return [ arg . length ] ;
} ) ;
myFunction ; // (arg: string)=>number[]
Extraire les schémas d'entrée et de sortie d'un schéma de fonction.
myFunction . parameters ( ) ;
// => ZodTuple<[ZodString, ZodNumber]>
myFunction . returnType ( ) ;
// => ZodBoolean
ZOD prend désormais en charge la coercition primitive sans avoir besoin de
.preprocess()
. Voir les documents de coercition pour plus d'informations.
En règle générale, ZOD fonctionne sous un paradigme "Parse puis transforme". ZOD valide d'abord l'entrée, puis le passe à travers une chaîne de fonctions de transformation. (For more information about transforms, read the .transform docs.)
Mais parfois, vous voulez appliquer une certaine transformation à l'entrée avant que l'analyse ne se produise. Un cas d'utilisation commun: Type Coercion. Zod le permet avec le z.preprocess()
.
const castToString = z . preprocess ( ( val ) => String ( val ) , z . string ( ) ) ;
Cela renvoie une instance ZodEffects
. ZodEffects
est une classe de wrapper qui contient toute logique concernant le prétraitement, les raffinements et les transformations.
Vous pouvez créer un schéma ZOD pour n'importe quel type de type TypeScript en utilisant z.custom()
. Ceci est utile pour créer des schémas pour des types qui ne sont pas pris en charge par ZOD hors de la boîte, tels que les littéraux de chaîne de modèles.
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;
Si vous ne fournissez pas de fonction de validation, ZOD autorisera toute valeur. Cela peut être dangereux!
z . custom < { arg : string } > ( ) ; // performs no validation
Vous pouvez personnaliser le message d'erreur et d'autres options en passant un deuxième argument. Ce paramètre fonctionne de la même manière que le paramètre des params de .refine
.
z . custom < ... > ( ( val ) => ... , "custom error message" ) ;
Tous les schémas ZOD contiennent certaines méthodes.
.parse
.parse(data: unknown): T
Compte tenu de tout schéma ZOD, vous pouvez appeler sa méthode .parse
pour vérifier que data
sont valides. Si c'est le cas, une valeur est renvoyée avec des informations de type complet! Sinon, une erreur est lancée.
IMPORTANT: La valeur renvoyée par
.parse
est un clone profond de la variable que vous avez transmise.
const stringSchema = z . string ( ) ;
stringSchema . parse ( "fish" ) ; // => returns "fish"
stringSchema . parse ( 12 ) ; // throws error
.parseAsync
.parseAsync(data:unknown): Promise<T>
Si vous utilisez des raffinements ou des transformations asynchrones (plus sur ceux-ci plus tard), vous devrez utiliser .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; }
Si vous ne voulez pas que ZOD lance des erreurs lorsque la validation échoue, utilisez .safeParse
. Cette méthode renvoie un objet contenant les données analysées avec succès ou une instance ZoDerror contenant des informations détaillées sur les problèmes de validation.
stringSchema . safeParse ( 12 ) ;
// => { success: false; error: ZodError }
stringSchema . safeParse ( "billie" ) ;
// => { success: true; data: 'billie' }
Le résultat est une union discriminée , vous pouvez donc gérer très facilement les erreurs:
const result = stringSchema . safeParse ( "billie" ) ;
if ( ! result . success ) {
// handle error then return
result . error ;
} else {
// do something
result . data ;
}
.safeParseAsync
Alias:
.spa
Une version asynchrone de safeParse
.
await stringSchema . safeParseAsync ( "billie" ) ;
Pour plus de commodité, cela a été aliasé à .spa
:
await stringSchema . spa ( "billie" ) ;
.refine
.refine(validator: (data:T)=>any, params?: RefineParams)
Zod vous permet de fournir une logique de validation personnalisée via des raffinements . (Pour les fonctionnalités avancées comme la création de plusieurs problèmes et la personnalisation des codes d'erreur, voir .superRefine
.)
Zod a été conçu pour refléter le dactylographie aussi étroitement que possible. Mais il existe de nombreux «types de raffinement» soi-disant que vous souhaiterez peut-être vérifier qui ne peuvent pas être représentés dans le système de type TypeScript. Par exemple: vérifier qu'un nombre est un entier ou qu'une chaîne est une adresse e-mail valide.
Par exemple, vous pouvez définir une vérification de validation personnalisée sur n'importe quel schéma ZOD avec .refine
:
const myString = z . string ( ) . refine ( ( val ) => val . length <= 255 , {
message : "String can't be more than 255 characters" ,
} ) ;
️ Les fonctions de raffinement ne doivent pas lancer. Au lieu de cela, ils devraient renvoyer une valeur de fausset à l'échec du signal.
Comme vous pouvez le voir, .refine
prend deux arguments.
T
- le type inféré du schéma) et en renvoie any
. Any truthy value will pass validation. (Avant [email protected], la fonction de validation devait retourner un booléen.) 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 ;
} ;
Pour les cas avancés, le deuxième argument peut également être une fonction qui renvoie 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" } ) ;
Because you provided a path
parameter, the resulting error will be:
ZodError {
issues : [ {
"code" : "custom" ,
"path" : [ "confirm" ] ,
"message" : "Passwords don't match"
} ]
}
Les raffinements peuvent également être asynchronisés:
const userId = z . string ( ) . refine ( async ( id ) => {
// verify that ID exists in database
return true ;
} ) ;
️ If you use async refinements, you must use the.parseAsync
method to parse data! Sinon, Zod lancera une erreur.
Les transformations et les raffinements peuvent être entrelacés:
z . string ( )
. transform ( ( val ) => val . length )
. refine ( ( val ) => val > 25 ) ;
.superRefine
The .refine
method is actually syntactic sugar atop a more versatile (and verbose) method called superRefine
. Voici un exemple:
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.` ,
} ) ;
}
} ) ;
Vous pouvez ajouter autant de problèmes que vous le souhaitez. Si ctx.addIssue
n'est pas appelée lors de l'exécution de la fonction, la validation passe.
Normalement, les raffinements créent toujours des problèmes avec un code d'erreur ZodIssueCode.custom
, mais avec superRefine
il est possible de lancer des problèmes de tout ZodIssueCode
. Chaque code de problème est décrit en détail dans le guide de gestion des erreurs: error_handling.md.
Par défaut, l'analyse se poursuivra même après la rupture d'une vérification de raffinement. Par exemple, si vous enchaînez plusieurs raffinements, ils seront tous exécutés. Cependant, il peut être souhaitable d' abandonner tôt pour empêcher l'exécution des raffinements ultérieurs. Pour y parvenir, passez le drapeau fatal
à ctx.addIssue
et retournez 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" ,
} ) ;
}
} ) ;
Si vous fournissez un prédicat de type à .refine()
ou .superRefine()
, le type résultant sera réduit au type de votre prédicat. Ceci est utile si vous mélangez plusieurs raffinements et transformations enchaînées:
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`!" ) ;
️ Vous devez utiliserctx.addIssue()
au lieu de renvoyer une valeur booléenne pour indiquer si la validation passe. Sictx.addIssue
n'est pas appelée lors de l'exécution de la fonction, la validation passe.
.transform
Pour transformer les données après l'analyse, utilisez la méthode transform
.
const stringToNumber = z . string ( ) . transform ( ( val ) => val . length ) ;
stringToNumber . parse ( "string" ) ; // => 6
Notez que stringToNumber
ci-dessus est une instance de la sous-classe ZodEffects
. Ce n'est pas une instance de ZodString
. Si vous souhaitez utiliser les méthodes intégrées de ZodString
(par exemple .email()
), vous devez appliquer ces méthodes avant toute transformation.
const emailToDomain = z
. string ( )
. email ( )
. transform ( ( val ) => val . split ( "@" ) [ 1 ] ) ;
emailToDomain . parse ( "[email protected]" ) ; // => example.com
La méthode .transform
peut simultanément valider et transformer la valeur. Ceci est souvent plus simple et moins duplicatif que transform
et refine
de chaînage.
Comme pour .superRefine
, la fonction de transformation reçoit un objet ctx
avec une méthode addIssue
qui peut être utilisée pour enregistrer les problèmes de validation.
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 ;
} ) ;
Les transformations et les raffinements peuvent être entrelacés. Celles-ci seront exécutées dans l'ordre elles sont déclarées.
const nameToGreeting = z
. string ( )
. transform ( ( val ) => val . toUpperCase ( ) )
. refine ( ( val ) => val . length > 15 )
. transform ( ( val ) => `Hello ${ val } ` )
. refine ( ( val ) => val . indexOf ( "!" ) === - 1 ) ;
Les transformations peuvent également être asynchrones.
const IdToUser = z
. string ( )
. uuid ( )
. transform ( async ( id ) => {
return await getUserById ( id ) ;
} ) ;
️ Si votre schéma contient des transformations asynchrones, vous devez utiliser .paSeasync () ou .SafeParsEasync () pour analyser les données. Sinon, Zod lancera une erreur.
.default
Vous pouvez utiliser des transformations pour implémenter le concept de "valeurs par défaut" dans ZOD.
const stringWithDefault = z . string ( ) . default ( "tuna" ) ;
stringWithDefault . parse ( undefined ) ; // => "tuna"
Facultativement, vous pouvez transmettre une fonction dans .default
qui sera réexécutée chaque fois qu'une valeur par défaut doit être générée:
const numberWithRandomDefault = z . number ( ) . default ( Math . random ) ;
numberWithRandomDefault . parse ( undefined ) ; // => 0.4413456736055323
numberWithRandomDefault . parse ( undefined ) ; // => 0.1871840107401901
numberWithRandomDefault . parse ( undefined ) ; // => 0.7223408162401552
Conceptuellement, c'est ainsi que Zod traite les valeurs par défaut:
undefined
, la valeur par défaut est renvoyée.describe
Utilisez .describe()
pour ajouter une propriété description
au schéma résultant.
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…
This can be useful for documenting a field, for example in a JSON Schema using a library like zod-to-json-schema
).
.catch
Utilisez .catch()
pour fournir une "valeur de capture" à retourner en cas d'erreur d'analyse.
const numberWithCatch = z . number ( ) . catch ( 42 ) ;
numberWithCatch . parse ( 5 ) ; // => 5
numberWithCatch . parse ( "tuna" ) ; // => 42
Facultativement, vous pouvez transmettre une fonction dans .catch
qui sera réexécutée chaque fois qu'une valeur par défaut doit être générée. A ctx
object containing the caught error will be passed into this function.
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
Conceptuellement, c'est ainsi que Zod traite les «valeurs de capture»:
.optional
Une méthode de commodité qui renvoie une version facultative d'un schéma.
const optionalString = z . string ( ) . optional ( ) ; // string | undefined
// equivalent to
z . optional ( z . string ( ) ) ;
.nullable
Une méthode de commodité qui renvoie une version nullable d'un schéma.
const nullableString = z . string ( ) . nullable ( ) ; // string | null
// equivalent to
z . nullable ( z . string ( ) ) ;
.nullish
A convenience method that returns a "nullish" version of a schema. Les schémas nullish accepteront à la fois undefined
et null
. En savoir plus sur le concept de "Nullish" dans les notes de version de type 3,7.
const nullishString = z . string ( ) . nullish ( ) ; // string | null | undefined
// equivalent to
z . string ( ) . nullable ( ) . optional ( ) ;
.array
Une méthode de commodité qui renvoie un schéma de tableau pour le type donné:
const stringArray = z . string ( ) . array ( ) ; // string[]
// equivalent to
z . array ( z . string ( ) ) ;
.promise
Une méthode de commodité pour les types de promesses:
const stringPromise = z . string ( ) . promise ( ) ; // Promise<string>
// equivalent to
z . promise ( z . string ( ) ) ;
.or
Une méthode de commodité pour les types d'union.
const stringOrNumber = z . string ( ) . or ( z . number ( ) ) ; // string | number
// equivalent to
z . union ( [ z . string ( ) , z . number ( ) ] ) ;
.and
Une méthode de commodité pour créer des types d'intersection.
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>
Le système de type TypeScript est structurel, ce qui signifie que deux types structurellement équivalents sont considérés comme les mêmes.
type Cat = { name : string } ;
type Dog = { name : string } ;
const petCat = ( cat : Cat ) => { } ;
const fido : Dog = { name : "fido" } ;
petCat ( fido ) ; // works fine
Dans certains cas, il peut être souhaitable de simuler le typage nominal dans TypeScript. Par exemple, vous souhaiterez peut-être écrire une fonction qui n'accepte qu'une entrée qui a été validée par ZOD. Cela peut être réalisé avec des types de marque (aka types opaques ).
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" } ) ;
Sous le capot, cela fonctionne en attachant une "marque" au type inféré à l'aide d'un type d'intersection. De cette façon, les structures de données simples / sans marque ne sont plus attribuables au type inféré du schéma.
const Cat = z . object ( { name : z . string ( ) } ) . brand < "Cat" > ( ) ;
type Cat = z . infer < typeof Cat > ;
// {name: string} & {[symbol]: "Cat"}
Notez que les types de marque n'affectent pas le résultat d'exécution de .parse
. C'est une construction statique uniquement.
.readonly
.readonly() => ZodReadonly<this>
Cette méthode renvoie une instance de schéma ZodReadonly
qui analyse l'entrée à l'aide du schéma de base, puis appelle Object.freeze()
sur le résultat. Le type inféré est également marqué comme 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
Le type inféré utilise des types de lecture intégrés de TypeScript le cas échéant.
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
Les schémas peuvent être enchaînés en "pipelines" de validation. Il est utile pour valider facilement le résultat après un .transform()
:
z . string ( )
. transform ( ( val ) => val . length )
. pipe ( z . number ( ) . min ( 5 ) ) ;
La méthode .pipe()
renvoie une instance ZodPipeline
.
.pipe()
pour résoudre les problèmes courants avec z.coerce
. Vous pouvez contraindre l'entrée aux types qui fonctionnent bien avec la coercition choisie. Utilisez ensuite .pipe()
pour appliquer la coercition.
sans entrée contrainte:
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
avec entrée contrainte:
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
Vous pouvez également utiliser cette technique pour éviter les coercitions qui lancent des erreurs non apprises.
sans entrée contrainte:
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
avec entrée contrainte:
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
Et les transformations?
In reality each Zod schema internally tracks two types: an input and an output. Pour la plupart des schémas (par exemple z.string()
), ces deux sont les mêmes. Mais une fois que vous ajoutez des transformations en mélange, ces deux valeurs peuvent diverger. For instance z.string().transform(val => val.length)
has an input of string
and an output of number
.
Vous pouvez extraire séparément les types d'entrée et de sortie comme tel:
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
With TypeScript generics, you can write reusable functions that accept Zod schemas as parameters. Cela vous permet de créer une logique de validation personnalisée, des transformations de schéma, etc., tout en maintenant la sécurité et l'inférence du type.
Lorsque vous essayez d'écrire une fonction qui accepte un schéma ZOD comme une entrée, il est tentant d'essayer quelque chose comme ceci:
function inferSchema < T > ( schema : z . ZodType < T > ) {
return schema ;
}
Cette approche est incorrecte et limite la capacité de TypeScript à déduire correctement l'argument. Peu importe ce que vous transmettez, le type de schema
sera une instance de ZodType
.
inferSchema ( z . string ( ) ) ;
// => ZodType<string>
This approach loses type information, namely which subclass the input actually is (in this case, ZodString
). That means you can't call any string-specific methods like .min()
on the result of inferSchema
.
A better approach is to infer the schema as a whole instead of merely its inferred type. 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