https://zod.dev
Validación de esquema de TypeScript-First con inferencia de tipo estático
Estos documentos han sido traducidos al chino.
npm
(nodo/bollo)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()
para solucionar problemas comunes con z.coerce
. ZOD es una biblioteca de Declaración y Validación de esquema TypeScript-First. Estoy usando el término "esquema" para referir ampliamente a cualquier tipo de datos, desde una string
simple hasta un objeto anidado complejo.
Zod está diseñado para ser lo más amigable para el desarrollador posible. El objetivo es eliminar las declaraciones de tipo duplicada. Con Zod, declara un validador una vez y Zod inferirá automáticamente el tipo de mecanografiado estático. Es fácil componer tipos más simples en estructuras de datos complejas.
Algunos otros grandes aspectos:
.optional()
) Devuelve una nueva instanciaEl patrocinio en cualquier nivel es apreciado y alentado. Si construyó un producto pagado con ZOD, considere uno de los niveles corporativos.
La plataforma de gestión de usuarios más completa
empleado.com
|
|
|
|
|
Propela | Cerbos | Escalar | Gatillo.dev |
TransLoadit | Infiso | Whop | Criptojobslist |
Plano. | Más grande | StoryBlok | Mux |
Brandon Bayer | Jiří Brabec | Alex Johansson | Sistemas fungibles |
Adaptable | Billetera de avana | Jason Lengstorf | Global Illumination, Inc. |
Maestro | Ryan Palmer | Michael Sweeney | NextBase |
Remoción | Connor Sinnott | Mohammad-Ali a'râbi | Supatool |
¡Hay un número creciente de herramientas que se construyen sobre o admiten Zod de forma nativa! Si ha creado una herramienta o biblioteca además de Zod, cuénteme en Twitter o comience una discusión. Lo agregaré a continuación y lo twittearé.
tRPC
: construya API typesfe de extremo a extremo sin GraphQL.@anatine/zod-nestjs
: métodos auxiliares para usar Zod en un proyecto Nestjs.zod-endpoints
: CONTRATOS Primeros puntos finales estrictamente escritos con ZOD. Openapi compatible.zhttp
: una biblioteca HTTP compatible con OpenAPI, estrictamente escrita, con la validación de entrada y respuesta de ZOD.domain-functions
: desacoplar la lógica de su negocio desde su marco utilizando funciones compuestas. Con inferencia de tipo de primera clase de extremo a extremo con esquemas Zod.@zodios/core
: un cliente API de TypeScript con tiempo de ejecución y validación de tiempo de compilación respaldada por Axios y Zod.express-zod-api
: construya API basadas en Express con validación de esquema de E/S y artículos intermedios personalizados.tapiduck
: API JSON TypeSafe de extremo a extremo con Zod y Express; Un poco como TRPC, pero más simple.koa-zod-router
: cree rutas TypeFe en KOA con validación de E/S usando ZOD.zod-sockets
: Microframework de Socket.io de ZOD con validación de E/S y especificaciones de asyncapi incorporadas react-hook-form
: un resolución de ZOD de primera parte para la forma de gancho React.zod-validation-error
: Genere mensajes de error fáciles de usar desde ZodError
s.zod-formik-adapter
: un adaptador Formik mantenido por la comunidad para Zod.react-zorm
: generación y validación <form>
para reaccionar usando ZOD.zodix
: Utilidades Zod para FormData y UrlSearchParams en cargadores y acciones de remix.conform
: una biblioteca de validación de formulario TypeFe para la mejora progresiva de los formularios HTML. Funciona con Remix y Next.js.remix-params-helper
: simplifique la integración de ZOD con URLSearchParams estándar y FormData para aplicaciones Remix.formik-validator-zod
: Biblioteca de validador compatible con Formik que simplifica el uso de Zod con Formik.zod-i18n-map
: útil para traducir los mensajes de error Zod.@modular-forms/solid
: Biblioteca de formularios modulares para SOLIDJS que admite ZOD para la validación.houseform
: una biblioteca React Form que usa ZOD para la validación.sveltekit-superforms
: biblioteca de formulario sobrealimentado para sveltekit con validación de Zod.mobx-zod-form
: Data-First Form Builder basado en MOBX y ZOD.@vee-validate/zod
: biblioteca de formulario para vue.js con validación de esquema Zod.zod-form-renderer
: campos de forma automática de infierno del esquema ZOD y renderizarlos con forma reaccionista con seguridad de tipo E2E. zod-to-ts
: Genere definiciones mecanografiadas a partir de esquemas Zod.zod-to-json-schema
: Convierta sus esquemas Zod en esquemas JSON.@anatine/zod-openapi
: convierte un esquema Zod en un SchemaObject
Openapi v3.x.zod-fast-check
: Genere arbitrarios fast-check
a partir de esquemas Zod.zod-dto
: Genere nest.js DTO de un esquema Zod.fastify-type-provider-zod
: cree proveedores de tipo Fastify a partir de esquemas Zod.zod-to-openapi
: Genere documentos completos de Openapi (Swagger) de Zod, incluidos esquemas, puntos finales y parámetros.nestjs-graphql-zod
: genera clases de modelos Nestjs GraphQL a partir de esquemas Zod. Proporciona decoradores de métodos GraphQL que trabajan con esquemas Zod.zod-openapi
: Crear documentación completa de Openapi v3.x a partir de esquemas Zod.fastify-zod-openapi
: proveedor de tipo Fastify, validación, serialización y @Soporte de Fastify/Swagger para esquemas Zod.typeschema
: adaptador universal para la validación del esquema.zodex
: (DE) Serialización para esquemas de Zod ts-to-zod
: Convierta las definiciones de TypeScript en esquemas Zod.@runtyping/zod
: Genere Zod a partir de tipos estáticos y esquema JSON.json-schema-to-zod
: Convierta sus esquemas JSON en esquemas Zod. Demostración en vivo.json-to-zod
: Convierta los objetos JSON en esquemas Zod. Demostración en vivo.graphql-codegen-typescript-validation-schema
: complemento GraphQL Code Generator para generar esquema de validación de formulario a partir de su esquema GraphQL.zod-prisma
: Genere esquemas de Zod a partir de su esquema de prisma.Supervillain
: Genere esquemas Zod a partir de sus estructuras Go.prisma-zod-generator
: emitir esquemas de Zod de su esquema de Prisma.drizzle-zod
: emitir esquemas Zod de su esquema de llovizna.prisma-trpc-generator
: emite enrutadores TRPC totalmente implementados y sus esquemas de validación utilizando ZOD.zod-prisma-types
crean tipos de ZOD a partir de sus modelos Prisma.quicktype
: Convertir objetos JSON y esquemas JSON en esquemas Zod.@sanity-typed/zod
: generar esquemas Zod a partir de esquemas de cordura.java-to-zod
: Convertir Pojos en esquemas de ZodOrval
: Genere esquemas Zod a partir de esquemas de OpenapiKubb
: Genere SDK y esquemas Zod a partir de sus esquemas de Openapi @anatine/zod-mock
: genere datos simulados a partir de un esquema Zod. Impulsado por Faker.js.zod-mocking
: Genere datos simulados de sus esquemas Zod.zod-fixture
: Use sus esquemas Zod para automatizar la generación de accesorios de prueba no relevantes de manera determinista.zocker
: genere datos simulados plausibles a partir de sus esquemas.zodock
genera datos simulados basados en esquemas Zod.zod-schema-faker
genera datos simulados de los esquemas Zod. Autorizado por @Faker-JS/Faker y Randexp.js freerstore
: Optimizador de costos de Firestore.slonik
: Node.js Postgres Client con una fuerte integración de Zod.schemql
: mejora su flujo de trabajo SQL combinando SQL sin procesar con seguridad de tipo objetivo y validación de esquema.soly
: cree aplicaciones CLI con Zod.pastel
: cree aplicaciones CLI con React, Zod y Ink.zod-xlsx
: un validador de recursos basado en XLSX utilizando esquemas Zod.znv
: análisis y validación de entorno a prueba de tipo para nodo.js con esquemas Zod.zod-config
: Cargue las configuraciones en múltiples fuentes con adaptadores flexibles, asegurando la seguridad de tipo con Zod.unplugin-environment
: un complemento para cargar variables de entorno de forma segura con validación de esquemas, simple con módulo virtual, tipo seguro con IntelliSense y mejor DX? Impulsado por Zod. zod_utilz
: Utilidades agnósticas marco para Zod.zod-playground
: una herramienta para aprender y probar funcionalidades de validación del esquema Zod. Enlace.zod-sandbox
: entorno controlado para probar esquemas Zod. Demostración en vivo.zod-dev
: desactiva condicionalmente el análisis de tiempo de ejecución Zod en producción.zod-accelerator
: acelera el rendimiento de Zod hasta ~ 100x. TypeScript 4.5+!
Debe habilitar el modo strict
en su tsconfig.json
. Esta es una mejor práctica para todos los proyectos de mecanografiado.
// tsconfig.json
{
// ...
"compilerOptions" : {
// ...
"strict" : true
}
}
npm
(nodo/bollo) npm install zod # npm
yarn add zod # yarn
bun add zod # bun
pnpm add zod # pnpm
Zod también publica una versión canaria en cada compromiso. Para instalar el canario:
npm install zod@canary # npm
yarn add zod@canary # yarn
bun add zod@canary # bun
pnpm add zod@canary # pnpm
deno.land/x
(Deno)A diferencia de Node, Deno se basa en las importaciones de URL directas en lugar de un administrador de paquetes como NPM. Zod está disponible en Deno.land/x. La última versión se puede importar así:
import { z } from "https://deno.land/x/zod/mod.ts" ;
También puede especificar una versión en particular:
import { z } from "https://deno.land/x/[email protected]/mod.ts" ;
El resto de este ReadMe supone que está utilizando NPM e importando directamente desde el paquete
"zod"
.
Creando un esquema de cadena 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 }
Creando un esquema de objetos
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 ahora proporciona una forma más conveniente de coaccionar los valores primitivos.
const schema = z . coerce . string ( ) ;
schema . parse ( "tuna" ) ; // => "tuna"
schema . parse ( 12 ) ; // => "12"
Durante el paso de análisis, la entrada se pasa a través de la función String()
, que es un JavaScript incorporado para coaccionar datos en cadenas.
schema . parse ( 12 ) ; // => "12"
schema . parse ( true ) ; // => "true"
schema . parse ( undefined ) ; // => "undefined"
schema . parse ( null ) ; // => "null"
El esquema devuelto es una instancia ZodString
normal, por lo que puede usar todos los métodos de cadena.
z . coerce . string ( ) . email ( ) . min ( 5 ) ;
Cómo funciona la coerción
Todos los tipos primitivos admiten coerción. Zod coacciona todas las entradas utilizando los constructores incorporados: 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)
Nota : la coerción booleana con z.coerce.boolean()
puede no funcionar como espere. Cualquier valor de verdad se coacciona a true
, y cualquier valor 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
Para obtener más control sobre la lógica de coerción, considere usar z.preprocess
o z.pipe()
.
Los esquemas literales representan un tipo literal, como "hello world"
o 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"
Actualmente no hay soporte para literales de fecha en Zod. Si tiene un caso de uso para esta función, presente un problema.
Zod incluye un puñado de validaciones específicas de cadena.
// 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 ( ) ;
Consulte Validator.js para obtener un montón de otras funciones de validación de cadenas útiles que se pueden usar junto con los refinamientos.
Puede personalizar algunos mensajes de error comunes al crear un esquema de cadena.
const name = z . string ( {
required_error : "Name is required" ,
invalid_type_error : "Name must be a string" ,
} ) ;
Al usar métodos de validación, puede aprobar un argumento adicional para proporcionar un mensaje de error personalizado.
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" } ) ;
Como habrá notado, Zod String incluye algunas validaciones relacionadas con la fecha/hora. Estas validaciones se basan en la expresión regular, por lo que no son tan estrictas como una biblioteca completa de fecha/hora. Sin embargo, son muy convenientes para validar la entrada del usuario.
El método z.string().datetime()
hace cumplir ISO 8601; El valor predeterminado no es compensaciones de zona horaria y precisión arbitraria de decimales sub-segundo.
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)
Las compensaciones de la zona horaria se pueden permitir configurando la opción offset
en 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)
También puede limitar la precision
permitida. Por defecto, se admite la precisión arbitraria de sub-segundo (pero opcional).
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
Agregado en Zod 3.23
El método z.string().date()
valida las cadenas en el formato 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
Agregado en Zod 3.23
El método z.string().time()
valida las cadenas en el formato HH:MM:SS[.s+]
. El segundo puede incluir precisión decimal arbitraria. No permite compensaciones de zona horaria de ningún tipo.
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)
Puede establecer la opción de precision
para restringir la precisión decimal permitida.
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
El método z.string().ip()
Valide IPv4 e 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
También puede configurar 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
Puede personalizar ciertos mensajes de error al crear un esquema de números.
const age = z . number ( {
required_error : "Age is required" ,
invalid_type_error : "Age must be a number" ,
} ) ;
Zod incluye un puñado de validaciones de número específicas.
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
Opcionalmente, puede pasar en un segundo argumento para proporcionar un mensaje de error personalizado.
z . number ( ) . lte ( 5 , { message : "this?is?too?big" } ) ;
Zod incluye un puñado de validaciones específicas de 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.
Puede personalizar ciertos mensajes de error al crear un esquema NAN.
const isNaN = z . nan ( {
required_error : "isNaN is required" ,
invalid_type_error : "isNaN must be 'not a number'" ,
} ) ;
Puede personalizar ciertos mensajes de error al crear un esquema booleano.
const isActive = z . boolean ( {
required_error : "isActive is required" ,
invalid_type_error : "isActive must be a boolean" ,
} ) ;
Use Z.Date () para validar las instancias Date
.
z . date ( ) . safeParse ( new Date ( ) ) ; // success: true
z . date ( ) . safeParse ( "2022-01-12T00:00:00.000Z" ) ; // success: false
Puede personalizar ciertos mensajes de error al crear un esquema de fecha.
const myDateSchema = z . date ( {
required_error : "Please select a date and time" ,
invalid_type_error : "That's not a date!" ,
} ) ;
Zod proporciona un puñado de validaciones específicas de fecha.
z . date ( ) . min ( new Date ( "1900-01-01" ) , { message : "Too old" } ) ;
z . date ( ) . max ( new Date ( ) , { message : "Too young!" } ) ;
Coerción hasta la fecha
Desde Zod 3.20, use z.coerce.date()
para pasar la entrada a través de 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
Para versiones más antiguas de ZOD, use z.preprocess
como se describe en este hilo.
const FishEnum = z . enum ( [ "Salmon" , "Tuna" , "Trout" ] ) ;
type FishEnum = z . infer < typeof FishEnum > ;
// 'Salmon' | 'Tuna' | 'Trout'
z.enum
es una forma nativa de ZOD de declarar un esquema con un conjunto fijo de valores de cadena permitidos. Pase la matriz de valores directamente a z.enum()
. Alternativamente, use as const
para definir sus valores de enum como una tupla de cadenas. Consulte los documentos de afirmación constante para más detalles.
const VALUES = [ "Salmon" , "Tuna" , "Trout" ] as const ;
const FishEnum = z . enum ( VALUES ) ;
Esto no está permitido, ya que Zod no puede inferir los valores exactos de cada elemento.
const fish = [ "Salmon" , "Tuna" , "Trout" ] ;
const FishEnum = z . enum ( fish ) ;
.enum
Para obtener autocompletación con un Zod Enum, use la propiedad .enum
de su esquema:
FishEnum . enum . Salmon ; // => autocompletes
FishEnum . enum ;
/*
=> {
Salmon: "Salmon",
Tuna: "Tuna",
Trout: "Trout",
}
*/
También puede recuperar la lista de opciones como tupla con la propiedad .options
:
FishEnum . options ; // ["Salmon", "Tuna", "Trout"];
.exclude/.extract()
Puede crear subconjuntos de un Zod enum con los métodos .exclude
y .extract
.
const FishEnum = z . enum ( [ "Salmon" , "Tuna" , "Trout" ] ) ;
const SalmonAndTrout = FishEnum . extract ( [ "Salmon" , "Trout" ] ) ;
const TunaOnly = FishEnum . exclude ( [ "Salmon" , "Trout" ] ) ;
Zod enums es el enfoque recomendado para definir y validar a Enums. Pero si necesita validar contra un enum de una biblioteca de terceros (o no desea reescribir sus enums existentes), puede usar z.nativeEnum()
.
Enums numéricos
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
Enumeros de cadena
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
Constante
La función .nativeEnum()
también funciona as const
.as const
requiere 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
Puede acceder al objeto subyacente con la propiedad .enum
:
FruitEnum . enum . Apple ; // "apple"
Puede hacer que cualquier esquema sea opcional con z.optional()
. Esto envuelve el esquema en una instancia ZodOptional
y devuelve el resultado.
const schema = z . optional ( z . string ( ) ) ;
schema . parse ( undefined ) ; // => returns undefined
type A = z . infer < typeof schema > ; // string | undefined
Por conveniencia, también puede llamar al método .optional()
en un esquema existente.
const user = z . object ( {
username : z . string ( ) . optional ( ) ,
} ) ;
type C = z . infer < typeof user > ; // { username?: string | undefined };
Puede extraer el esquema envuelto de una instancia ZodOptional
con .unwrap()
.
const stringSchema = z . string ( ) ;
const optionalString = stringSchema . optional ( ) ;
optionalString . unwrap ( ) === stringSchema ; // true
Del mismo modo, puede crear tipos anulables con z.nullable()
.
const nullableString = z . nullable ( z . string ( ) ) ;
nullableString . parse ( "asdf" ) ; // => "asdf"
nullableString . parse ( null ) ; // => null
O use el método .nullable()
.
const E = z . string ( ) . nullable ( ) ; // equivalent to nullableString
type E = z . infer < typeof E > ; // string | null
Extraiga el esquema interno con .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
Use .shape
para acceder a los esquemas para una clave en particular.
Dog . shape . name ; // => string schema
Dog . shape . age ; // => number schema
.keyof
Use .keyof
para crear un esquema ZodEnum
a partir de las teclas de un esquema de objeto.
const keySchema = Dog . keyof ( ) ;
keySchema ; // ZodEnum<["name", "age"]>
.extend
Puede agregar campos adicionales a un esquema de objeto con el método .extend
.
const DogWithBreed = Dog . extend ( {
breed : z . string ( ) ,
} ) ;
¡Puede usar .extend
para sobrescribir los campos! ¡Ten cuidado con este poder!
.merge
Equivalente a 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 los dos esquemas comparten claves, las propiedades de B anulan la propiedad de A. El esquema devuelto también hereda la política "Desconocida Keeys" (Strip/Strict/Passthrough) y el esquema Catchall de B.
.pick/.omit
Inspirado en los tipos de utilidad Omit
Pick
incorporados de TypeScript, todos los esquemas de objetos Zod tienen métodos .pick
.omit
que devuelven una versión modificada. Considere este esquema de recetas:
const Recipe = z . object ( {
id : z . string ( ) ,
name : z . string ( ) ,
ingredients : z . array ( z . string ( ) ) ,
} ) ;
Para mantener solo ciertas claves, use .pick
.
const JustTheName = Recipe . pick ( { name : true } ) ;
type JustTheName = z . infer < typeof JustTheName > ;
// => { name: string }
Para eliminar ciertas claves, use .omit
.
const NoIDRecipe = Recipe . omit ( { id : true } ) ;
type NoIDRecipe = z . infer < typeof NoIDRecipe > ;
// => { name: string, ingredients: string[] }
.partial
Inspirado en el tipo de utilidad TypeScript incorporado parcial, el método .partial
hace que todas las propiedades sean opcionales.
Comenzando desde este objeto:
const user = z . object ( {
email : z . string ( ) ,
username : z . string ( ) ,
} ) ;
// { email: string; username: string }
Podemos crear una versión parcial:
const partialUser = user . partial ( ) ;
// { email?: string | undefined; username?: string | undefined }
También puede especificar qué propiedades hacer opcional:
const optionalEmail = user . partial ( {
email : true ,
} ) ;
/*
{
email?: string | undefined;
username: string
}
*/
.deepPartial
El método .partial
es superficial: solo se aplica un nivel de profundidad. También hay una versión "profunda":
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}[]
}
*/
Limitación importante: los parciales profundos solo funcionan como se esperaba en jerarquías de objetos, matrices y tuplas.
.required
Contrariamente al método .partial
, el método .required
hace que todas las propiedades sean necesarias.
Comenzando desde este objeto:
const user = z
. object ( {
email : z . string ( ) ,
username : z . string ( ) ,
} )
. partial ( ) ;
// { email?: string | undefined; username?: string | undefined }
Podemos crear una versión requerida:
const requiredUser = user . required ( ) ;
// { email: string; username: string }
También puede especificar qué propiedades se requiere:
const requiredEmail = user . required ( {
email : true ,
} ) ;
/*
{
email: string;
username?: string | undefined;
}
*/
.passthrough
Por defecto, los esquemas de objetos Zod eliminan las claves no reconocidas durante el análisis.
const person = z . object ( {
name : z . string ( ) ,
} ) ;
person . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => { name: "bob dylan" }
// extraKey has been stripped
En su lugar, si desea pasar a través de claves desconocidas, use .passthrough()
.
person . passthrough ( ) . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => { name: "bob dylan", extraKey: 61 }
.strict
Por defecto, los esquemas de objetos Zod eliminan las claves no reconocidas durante el análisis. Puede rechazar las teclas desconocidas con .strict()
. Si hay claves desconocidas en la entrada, Zod lanzará un error.
const person = z
. object ( {
name : z . string ( ) ,
} )
. strict ( ) ;
person . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => throws ZodError
.strip
Puede usar el método .strip
para restablecer un esquema de objeto al comportamiento predeterminado (eliminando las claves no reconocidas).
.catchall
Puede pasar un esquema "Catchall" a un esquema de objetos. Todas las claves desconocidas serán validadas en su contra.
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
Usando .catchall()
evite .passthrough()
, .strip()
o .strict()
. Todas las claves ahora se consideran "conocidas".
const stringArray = z . array ( z . string ( ) ) ;
// equivalent
const stringArray = z . string ( ) . array ( ) ;
Tenga cuidado con el método .array()
. Devuelve una nueva instancia ZodArray
. Esto significa que el orden en el que llamas métodos es importante. Por ejemplo:
z . string ( ) . optional ( ) . array ( ) ; // (string | undefined)[]
z . string ( ) . array ( ) . optional ( ) ; // string[] | undefined
.element
Use .element
para acceder al esquema para un elemento de la matriz.
stringArray . element ; // => string schema
.nonempty
Si desea asegurarse de que una matriz contenga al menos un elemento, use .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
Opcionalmente, puede especificar un mensaje de error personalizado:
// 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
A diferencia de .nonempty()
estos métodos no cambian el tipo inferido.
A diferencia de las matrices, las tuplas tienen un número fijo de elementos y cada elemento puede tener un tipo diferente.
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 }]
Se puede agregar un argumento variádico ("reposo") con el método .rest
.
const variadicTuple = z . tuple ( [ z . string ( ) ] ) . rest ( z . number ( ) ) ;
const result = variadicTuple . parse ( [ "hello" , 1 , 2 , 3 ] ) ;
// => [string, ...number[]];
Zod incluye un método z.union
incorporado para componer "o" tipos.
const stringOrNumber = z . union ( [ z . string ( ) , z . number ( ) ] ) ;
stringOrNumber . parse ( "foo" ) ; // passes
stringOrNumber . parse ( 14 ) ; // passes
Zod probará la entrada contra cada una de las "opciones" en orden y devolverá el primer valor que se valida correctamente.
Para conveniencia, también puede usar el método .or
:
const stringOrNumber = z . string ( ) . or ( z . number ( ) ) ;
Validación de cadena opcional:
Para validar una entrada de formulario opcional, puede unir la validación de cadena deseada con una cadena vacía literal.
Este ejemplo valida una entrada que es opcional pero que debe contener una URL válida:
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
Un sindicato discriminado es una unión de esquemas de objetos que comparten una clave particular.
type MyUnion =
| { status : "success" ; data : string }
| { status : "failed" ; error : Error } ;
Tales sindicatos pueden representarse con el método z.discriminatedUnion
. Esto permite una evaluación más rápida, porque Zod puede verificar la clave del discriminador ( status
en el ejemplo anterior) para determinar qué esquema debe usarse para analizar la entrada. Esto hace que el análisis sea más eficiente y le permite a Zod informar errores más amigables.
Con el método de sindicato básico, la entrada se prueba con cada una de las "opciones" proporcionadas, y en el caso de la invalidez, los problemas para todas las "opciones" se muestran en el error ZOD. Por otro lado, el sindicato discriminado permite seleccionar solo una de las "opciones", probar en su contra y mostrar solo los problemas relacionados con esta "opción".
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" } ) ;
Puede extraer una referencia a la matriz de esquemas con la propiedad .options
.
myUnion . options ; // [ZodObject<...>, ZodObject<...>]
Para fusionar dos o más sindicatos discriminados, use .options
con la destrucción.
const A = z . discriminatedUnion ( "status" , [
/* options */
] ) ;
const B = z . discriminatedUnion ( "status" , [
/* options */
] ) ;
const AB = z . discriminatedUnion ( "status" , [ ... A . options , ... B . options ] ) ;
Los esquemas de registro se utilizan para validar tipos como Record<string, number>
. Esto es particularmente útil para almacenar o almacenar en caché de elementos por identificación.
const User = z . object ( { name : z . string ( ) } ) ;
const UserStore = z . record ( z . string ( ) , User ) ;
type UserStore = z . infer < typeof UserStore > ;
// => Record<string, { name: string }>
El esquema y el tipo inferido se pueden usar así:
const userStore : UserStore = { } ;
userStore [ "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd" ] = {
name : "Carlotta" ,
} ; // passes
userStore [ "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd" ] = {
whatever : "Ice cream sundae" ,
} ; // TypeError
Una nota sobre teclas numéricas
Mientras que z.record(keyType, valueType)
puede aceptar tipos de teclas numéricas y el tipo de registro incorporado de TypeScript es Record<KeyType, ValueType>
, es difícil representar el Record<number, any>
en Zod.
Como resultado, el comportamiento de TypeScript que rodea [k: number]
es un poco poco intuitivo:
const testMap : { [ k : number ] : string } = {
1 : "one" ,
} ;
for ( const key in testMap ) {
console . log ( ` ${ key } : ${ typeof key } ` ) ;
}
// prints: `1: string`
Como puede ver, JavaScript lanza automáticamente todas las claves de objetos para las cadenas debajo del capó. Dado que Zod está tratando de cerrar la brecha entre los tipos estáticos y de tiempo de ejecución, no tiene sentido proporcionar una forma de crear un esquema de registro con claves numéricas, ya que no existe una clave numérica en JavaScript de tiempo de ejecución.
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>
Los esquemas establecidos pueden limitarse aún más con los siguientes métodos de utilidad.
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
Las intersecciones son útiles para crear tipos "lógicos y". Esto es útil para intersectar dos tipos de objetos.
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 ) ;
Aunque en muchos casos, se recomienda usar A.merge(B)
para fusionar dos objetos. El método .merge
devuelve una nueva instancia ZodObject
, mientras que A.and(B)
devuelve una instancia ZodIntersection
menos útil que carece de métodos de objetos comunes como 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
Puede definir un esquema recursivo en Zod, pero debido a una limitación del mecanografiado, su tipo no puede inferirse estáticamente. En su lugar, deberá definir la definición de tipo manualmente y proporcionarla a Zod como una "pista de tipo".
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
Gracias a Crasite por este ejemplo.
Cuando se usa z.ZodType
con z.ZodEffects
( .refine
, .transform
, preprocess
, etc ...), deberá definir los tipos de entrada y salida del esquema. 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 ( ) ) ,
} ) ;
Gracias a Marcus13371337 y Joelbeeldi por este ejemplo.
Si desea validar cualquier valor JSON, puede usar el fragmento a continuación.
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 ) ;
Gracias a Ggoodman por sugerir esto.
A pesar de apoyar esquemas recursivos, pasar datos cíclicos a Zod causará un bucle infinito en algunos casos.
Para detectar objetos cíclicos antes de causar problemas, considere este enfoque.
const numberPromise = z . promise ( z . number ( ) ) ;
El "análisis" funciona de manera un poco diferente con los esquemas de promesa. La validación ocurre en dos partes:
.then
y .catch
)..then
para adjuntar un paso de validación adicional a la promesa existente. Tendrá que usar .catch
en la promesa devuelta de manejar fallas de validación. 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
} ;
Puede usar z.instanceof
para verificar que la entrada sea una instancia de una clase. Esto es útil para validar las entradas contra las clases que se exportan desde bibliotecas de terceros.
class Test {
name : string ;
}
const TestSchema = z . instanceof ( Test ) ;
const blob : any = "whatever" ;
TestSchema . parse ( new Test ( ) ) ; // passes
TestSchema . parse ( blob ) ; // throws
Zod también te permite definir "esquemas de funciones". Esto facilita la validación de las entradas y salidas de una función sin entremezclarse su código de validación y "lógica comercial".
Puede crear un esquema de funciones con z.function(args, returnType)
.
const myFunction = z . function ( ) ;
type myFunction = z . infer < typeof myFunction > ;
// => ()=>unknown
Definir entradas y salidas.
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
Los esquemas de funciones tienen un método .implement()
que acepta una función y devuelve una nueva función que valida automáticamente sus entradas y salidas.
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 solo le importa validar las entradas, simplemente no llame al método .returns()
. El tipo de salida se inferirá de la implementación.
Puede usar la opción
z.void()
especial si su función no devuelve nada. Esto permitirá que Zod inferirá correctamente el tipo de funciones que retornan el vacío. (Las funciones que retornan el vacío realmente devuelven indefinidos).
const myFunction = z
. function ( )
. args ( z . string ( ) )
. implement ( ( arg ) => {
return [ arg . length ] ;
} ) ;
myFunction ; // (arg: string)=>number[]
Extraiga los esquemas de entrada y salida de un esquema de funciones.
myFunction . parameters ( ) ;
// => ZodTuple<[ZodString, ZodNumber]>
myFunction . returnType ( ) ;
// => ZodBoolean
Zod ahora admite la coerción primitiva sin la necesidad de
.preprocess()
. Consulte los documentos de coerción para obtener más información.
Por lo general, Zod opera bajo un paradigma de "análisis y luego transformar". Zod valida la entrada primero, luego la pasa a través de una cadena de funciones de transformación. (Para obtener más información sobre las transformaciones, lea los documentos .Transform).
Pero a veces desea aplicar algo de transformación a la entrada antes de que ocurra el análisis. Un caso de uso común: tipo coerción. Zod habilita esto con el z.preprocess()
.
const castToString = z . preprocess ( ( val ) => String ( val ) , z . string ( ) ) ;
Esto devuelve una instancia ZodEffects
. ZodEffects
es una clase de envoltorio que contiene toda la lógica relacionada con el preprocesamiento, los refinamientos y las transformaciones.
Puede crear un esquema ZOD para cualquier tipo de mecanografía utilizando z.custom()
. Esto es útil para crear esquemas para tipos que no son compatibles con ZOD de la caja, como los literales de cadena de plantillas.
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 no proporciona una función de validación, Zod permitirá cualquier valor. ¡Esto puede ser peligroso!
z . custom < { arg : string } > ( ) ; // performs no validation
Puede personalizar el mensaje de error y otras opciones aprobando un segundo argumento. Este parámetro funciona de la misma manera que el parámetro de parámetros de .refine
.
z . custom < ... > ( ( val ) => ... , "custom error message" ) ;
Todos los esquemas Zod contienen ciertos métodos.
.parse
.parse(data: unknown): T
Dado cualquier esquema Zod, puede llamar a su método .parse
para verificar data
es válido. Si es así, ¡se devuelve un valor con información de tipo completo! De lo contrario, se lanza un error.
IMPORTANTE: El valor devuelto por
.parse
es un clon profundo de la variable en la que pasó.
const stringSchema = z . string ( ) ;
stringSchema . parse ( "fish" ) ; // => returns "fish"
stringSchema . parse ( 12 ) ; // throws error
.parseAsync
.parseAsync(data:unknown): Promise<T>
Si usa refinamientos o transformaciones asíncronas (más en las que más adelante), deberá usar .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 no desea que Zod arroje errores cuando falla la validación, use .safeParse
. Este método devuelve un objeto que contiene los datos analizados con éxito o una instancia de Zoderror que contiene información detallada sobre los problemas de validación.
stringSchema . safeParse ( 12 ) ;
// => { success: false; error: ZodError }
stringSchema . safeParse ( "billie" ) ;
// => { success: true; data: 'billie' }
El resultado es una unión discriminada , por lo que puede manejar errores muy convenientemente:
const result = stringSchema . safeParse ( "billie" ) ;
if ( ! result . success ) {
// handle error then return
result . error ;
} else {
// do something
result . data ;
}
.safeParseAsync
Alias:
.spa
Una versión asincrónica de safeParse
.
await stringSchema . safeParseAsync ( "billie" ) ;
Por conveniencia, esto ha sido alias a .spa
:
await stringSchema . spa ( "billie" ) ;
.refine
.refine(validator: (data:T)=>any, params?: RefineParams)
Zod le permite proporcionar una lógica de validación personalizada a través de refinamientos . (For advanced features like creating multiple issues and customizing error codes, see .superRefine
.)
Zod fue diseñado para reflejar el mecanografiado lo más cerca posible. Pero hay muchos llamados "tipos de refinamiento" que es posible que desee verificar que no se pueda representar en el sistema de tipos de TypeScript. Por ejemplo: verificar que un número es un entero o que una cadena es una dirección de correo electrónico válida.
Por ejemplo, puede definir una verificación de validación personalizada en cualquier esquema de Zod con .refine
:
const myString = z . string ( ) . refine ( ( val ) => val . length <= 255 , {
message : "String can't be more than 255 characters" ,
} ) ;
️ Las funciones de refinamiento no deberían lanzar. En cambio, deben devolver un valor de falsificación a la falla de señalización.
Como puede ver, .refine
toma dos argumentos.
T
- el tipo inferido del esquema) y devuelve any
. Cualquier valor de verdad pasará la validación. (Antes de [email protected], la función de validación tenía que devolver un booleano). 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 ;
} ;
Para casos avanzados, el segundo argumento también puede ser una función que devuelve 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" } ) ;
Debido a que proporcionó un parámetro path
, el error resultante será:
ZodError {
issues : [ {
"code" : "custom" ,
"path" : [ "confirm" ] ,
"message" : "Passwords don't match"
} ]
}
Los refinamientos también pueden ser asíncronos:
const userId = z . string ( ) . refine ( async ( id ) => {
// verify that ID exists in database
return true ;
} ) ;
️ Si usa refinamientos de Async, debe usar el método.parseAsync
para analizar los datos. De lo contrario, Zod lanzará un error.
Las transformaciones y los refinamientos se pueden entrelazar:
z . string ( )
. transform ( ( val ) => val . length )
. refine ( ( val ) => val > 25 ) ;
.superRefine
El método .refine
es en realidad azúcar sintáctica sobre un método más versátil (y verboso) llamado superRefine
. Aquí hay un ejemplo:
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.` ,
} ) ;
}
} ) ;
Puede agregar tantos problemas como desee. Si no se llama ctx.addIssue
durante la ejecución de la función, la validación pasa.
Normalmente, los refinamientos siempre ZodIssueCode
problemas con un código superRefine
error ZodIssueCode.custom
. Cada código de emisión se describe en detalle en la Guía de manejo de errores: ERROR_HANDLING.MD.
Por defecto, el análisis continuará incluso después de que falle una verificación de refinamiento. Por ejemplo, si encadenan múltiples refinamientos, todos serán ejecutados. Sin embargo, puede ser deseable abortar temprano para evitar que se ejecuten refinamientos posteriores. Para lograr esto, pase la bandera fatal
a ctx.addIssue
y regrese 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 proporciona un predicado de tipo a .refine()
o .superRefine()
, el tipo resultante se reducirá al tipo de su predicado. Esto es útil si está mezclando múltiples refinamientos y transformaciones encadenadas:
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`!" ) ;
️ Debe usarctx.addIssue()
en lugar de devolver un valor booleano para indicar si la validación pasa. Si no se llamactx.addIssue
durante la ejecución de la función, la validación pasa.
.transform
Para transformar datos después del análisis, use el método transform
.
const stringToNumber = z . string ( ) . transform ( ( val ) => val . length ) ;
stringToNumber . parse ( "string" ) ; // => 6
Tenga en cuenta que stringToNumber
anterior es una instancia de la subclase ZodEffects
. No es una instancia de ZodString
. Si desea utilizar los métodos incorporados de ZodString
(por ejemplo .email()
), debe aplicar esos métodos antes de que se transforme.
const emailToDomain = z
. string ( )
. email ( )
. transform ( ( val ) => val . split ( "@" ) [ 1 ] ) ;
emailToDomain . parse ( "[email protected]" ) ; // => example.com
El método .transform
puede validar y transformar simultáneamente el valor. This is often simpler and less duplicative than chaining transform
and refine
.
Al igual que con .superRefine
, la función de transformación recibe un objeto ctx
con un método addIssue
que puede usarse para registrar problemas de validación.
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 ;
} ) ;
Las transformaciones y los refinamientos pueden entrelazarse. Estos serán ejecutados en el orden en que se declaran.
const nameToGreeting = z
. string ( )
. transform ( ( val ) => val . toUpperCase ( ) )
. refine ( ( val ) => val . length > 15 )
. transform ( ( val ) => `Hello ${ val } ` )
. refine ( ( val ) => val . indexOf ( "!" ) === - 1 ) ;
Las transformaciones también pueden ser asíncronas.
const IdToUser = z
. string ( )
. uuid ( )
. transform ( async ( id ) => {
return await getUserById ( id ) ;
} ) ;
️ Si su esquema contiene transformaciones asíncronas, debe usar .Parseasync () o .SafePeSeAsync () para analizar los datos. De lo contrario, Zod lanzará un error.
.default
Puede usar transformaciones para implementar el concepto de "valores predeterminados" en Zod.
const stringWithDefault = z . string ( ) . default ( "tuna" ) ;
stringWithDefault . parse ( undefined ) ; // => "tuna"
Opcionalmente, puede pasar una función a .default
que se volverá a ejecutar siempre que se necesita generar un valor predeterminado:
const numberWithRandomDefault = z . number ( ) . default ( Math . random ) ;
numberWithRandomDefault . parse ( undefined ) ; // => 0.4413456736055323
numberWithRandomDefault . parse ( undefined ) ; // => 0.1871840107401901
numberWithRandomDefault . parse ( undefined ) ; // => 0.7223408162401552
Conceptualmente, así es como ZOD procesa valores predeterminados:
undefined
, se devuelve el valor predeterminado.describe
Use .describe()
para agregar una propiedad description
al esquema resultante.
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…
Esto puede ser útil para documentar un campo, por ejemplo, en un esquema JSON que usa una biblioteca como zod-to-json-schema
).
.catch
Use .catch()
para proporcionar un "valor de captura" para devolverse en caso de un error de análisis.
const numberWithCatch = z . number ( ) . catch ( 42 ) ;
numberWithCatch . parse ( 5 ) ; // => 5
numberWithCatch . parse ( "tuna" ) ; // => 42
Opcionalmente, puede pasar una función a .catch
que se volverá a ejecutar siempre que se necesita generar un valor predeterminado. Un objeto ctx
que contiene el error capturado se pasará a esta función.
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
Conceptualmente, así es como Zod procesa "valores de captura":
.optional
Un método de conveniencia que devuelve una versión opcional de un esquema.
const optionalString = z . string ( ) . optional ( ) ; // string | undefined
// equivalent to
z . optional ( z . string ( ) ) ;
.nullable
Un método de conveniencia que devuelve una versión anulable de un esquema.
const nullableString = z . string ( ) . nullable ( ) ; // string | null
// equivalent to
z . nullable ( z . string ( ) ) ;
.nullish
Un método de conveniencia que devuelve una versión "nula" de un esquema. Los esquemas nullish aceptarán tanto undefined
como null
. Lea más sobre el concepto de "nulo" en las notas de la versión TypeScript 3.7.
const nullishString = z . string ( ) . nullish ( ) ; // string | null | undefined
// equivalent to
z . string ( ) . nullable ( ) . optional ( ) ;
.array
Un método de conveniencia que devuelve un esquema de matriz para el tipo dado:
const stringArray = z . string ( ) . array ( ) ; // string[]
// equivalent to
z . array ( z . string ( ) ) ;
.promise
Un método de conveniencia para los tipos de promesas:
const stringPromise = z . string ( ) . promise ( ) ; // Promise<string>
// equivalent to
z . promise ( z . string ( ) ) ;
.or
Un método de conveniencia para los tipos de sindicatos.
const stringOrNumber = z . string ( ) . or ( z . number ( ) ) ; // string | number
// equivalent to
z . union ( [ z . string ( ) , z . number ( ) ] ) ;
.and
Un método de conveniencia para crear tipos de intersección.
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>
El sistema de tipos de TypeScript es estructural, lo que significa que dos tipos que son estructuralmente equivalentes se consideran iguales.
type Cat = { name : string } ;
type Dog = { name : string } ;
const petCat = ( cat : Cat ) => { } ;
const fido : Dog = { name : "fido" } ;
petCat ( fido ) ; // works fine
En algunos casos, puede ser deseable simular tipificación nominal dentro de TypeScript. Por ejemplo, es posible que desee escribir una función que solo acepte una entrada validada por Zod. Esto se puede lograr con tipos de marca (también conocido como tipos opacos ).
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" } ) ;
Debajo del capó, esto funciona uniendo una "marca" al tipo inferido utilizando un tipo de intersección. De esta manera, las estructuras de datos simples/sin marca ya no son asignables al tipo inferido del esquema.
const Cat = z . object ( { name : z . string ( ) } ) . brand < "Cat" > ( ) ;
type Cat = z . infer < typeof Cat > ;
// {name: string} & {[symbol]: "Cat"}
Tenga en cuenta que los tipos de marca no afectan el resultado del tiempo de ejecución de .parse
. Es una construcción solo estática.
.readonly
.readonly() => ZodReadonly<this>
Este método devuelve una instancia de esquema ZodReadonly
que analiza la entrada usando el esquema base, luego llama a Object.freeze()
en el resultado. El tipo inferido también está marcado como 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
El tipo inferido utiliza los tipos de lectura incorporados de TypeScript cuando es relevante.
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
Los esquemas se pueden encadenar a la validación de "tuberías". It's useful for easily validating the result after a .transform()
:
z . string ( )
. transform ( ( val ) => val . length )
. pipe ( z . number ( ) . min ( 5 ) ) ;
El método .pipe()
devuelve una instancia ZodPipeline
.
.pipe()
para solucionar problemas comunes con z.coerce
. Puede limitar la entrada a los tipos que funcionan bien con la coerción elegida. Luego use .pipe()
para aplicar la coerción.
sin entrada restringida:
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
con entrada restringida:
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
También puede usar esta técnica para evitar coerciones que arrojan errores no capturados.
sin entrada restringida:
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
con entrada restringida:
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
Puede extraer el tipo de mecanografiado de cualquier esquema con 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
¿Qué pasa con las transformaciones?
En realidad, cada esquema Zod rastrea internamente dos tipos: una entrada y una salida. For most schemas (eg z.string()
) these two are the same. Pero una vez que agregas transformaciones en la mezcla, estos dos valores pueden divergir. Por ejemplo z.string().transform(val => val.length)
tiene una entrada de string
y una salida de number
.
Puede extraer por separado los tipos de entrada y salida así:
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
Con los genéricos TypeScript, puede escribir funciones reutilizables que acepten esquemas Zod como parámetros. Esto le permite crear lógica de validación personalizada, transformaciones de esquemas y más, al tiempo que mantiene la seguridad e inferencia de tipo.
Al intentar escribir una función que acepte un esquema Zod como entrada, es tentador probar algo como esto:
function inferSchema < T > ( schema : z . ZodType < T > ) {
return schema ;
}
Este enfoque es incorrecto y limita la capacidad de TypeScript para inferir adecuadamente el argumento. No importa lo que pase, el tipo de schema
será una instancia de ZodType
.
inferSchema ( z . string ( ) ) ;
// => ZodType<string>
Este enfoque pierde información de tipo, a saber, qué subclase la entrada es realmente (en este caso, 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