https://zod.dev
Validação de esquema com TypeScript-primeiro
These docs have been translated into Chinese.
npm
(nó/pão)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 corrigir problemas comuns com z.coerce
. Zod é uma biblioteca de validação e declaração de esquema e primeiro esquema. Estou usando o termo "esquema" para me referir amplamente a qualquer tipo de dados, de uma string
simples para um objeto aninhado complexo.
O Zod foi projetado para ser o mais fácil de desenvolvedor possível. O objetivo é eliminar declarações de tipo duplicado. Com o ZOD, você declara um validador uma vez e Zod inferirá automaticamente o tipo de texto estático. É fácil compor tipos mais simples em estruturas de dados complexas.
Alguns outros grandes aspectos:
.optional()
O patrocínio em qualquer nível é apreciado e incentivado. If you built a paid product using Zod, consider one of the corporate tiers.
A plataforma de gerenciamento de usuários mais abrangente
Clerk.com
|
|
|
|
|
Propelauth | Cerbos | Escalar | Trigger.dev |
Transloadit | Infísico | WHOP | Criptojobslist |
Simples. | INSOURO | Storyblok | Mux |
Brandon Bayer | Jiří Brabec | Alex Johansson | Sistemas fungíveis |
Adaptável | Carteira Avana | Jason Lengstorf | Global Illumination, Inc. |
Masterborn | Ryan Palmer | Michael Sweeney | NextBase |
Remoção | Connor Sinnott | Mohammad-ali a'râbi | Supatool |
Há um número crescente de ferramentas que são construídas no topo ou apoiam o Zod nativamente! Se você construiu uma ferramenta ou biblioteca em cima do Zod, fale -me sobre isso no Twitter ou inicie uma discussão. Vou adicioná -lo abaixo e twittar.
tRPC
: Construa APIs Type-Afera de ponta a ponta sem grafql.@anatine/zod-nestjs
: Métodos Helper para usar o Zod em um projeto NestJS.zod-endpoints
: contrato primeiro digitados estritamente pontos de extremidade com Zod. OpenApi Compatível.zhttp
: Uma biblioteca HTTP compatível com o OpenAPI, rigorosamente digitada, com a validação de entrada e resposta ZOD.domain-functions
: desacoplam sua lógica de negócios da sua estrutura usando funções compostas. Com a inferência de primeira classe de ponta a ponta, alimentada por esquemas ZOD.@zodios/core
: um cliente da API do TypeScript com tempo de execução e validação de tempo de compilação apoiada por AXIOS e ZOD.express-zod-api
: Build APIs baseadas em Express com validação de esquema de E/S e middlewares personalizados.tapiduck
: End-to-end typesafe JSON APIs with Zod and Express; Um pouco como TRPC, mas mais simples.koa-zod-router
: Crie rotas TypeSafe em KOA com validação de E/S usando o Zod.zod-sockets
: Microframework de Zod Socket.io com validação de E/S e especificações de Asyncapi embutidas react-hook-form
: um resolvedor de zod de primeira parte para o reacto gancho de gancho.zod-validation-error
: gerar mensagens de erro amigáveis a partir de ZodError
s.zod-formik-adapter
: Um adaptador Formik mantido na comunidade para Zod.react-zorm
: geração independente <form>
e validação para reação usando Zod.zodix
: utilitários ZOD para FormData e URLSearchParams em carregadores e ações de remix.conform
: uma biblioteca de validação de formulário TypeAfe para aprimoramento progressivo dos formulários HTML. Trabalha com remix e next.js.remix-params-helper
: simplifique a integração do ZOD com o URLSearchParams padrão e o FormData para aplicativos Remix.formik-validator-zod
: Biblioteca de Validador compatível com Formik que simplifica o uso do Zod com o Formik.zod-i18n-map
: Útil para traduzir mensagens de erro do ZOD.@modular-forms/solid
: biblioteca de formulários modular para SOLIDJS que suporta Zod para validação.houseform
: uma biblioteca de formulários do React que usa Zod para validação.sveltekit-superforms
: biblioteca de formulários sobrealimentada para Sveltekit com a validação Zod.mobx-zod-form
: Data-First Form Builder baseado em Mobx & Zod.@vee-validate/zod
: biblioteca de formulários para vue.js com validação de esquema Zod.zod-form-renderer
: Os campos de formulário automático do esquema Zod e os renderizam com forma de reacto com segurança do tipo E2E. zod-to-ts
: Gere definições de texto digital de esquemas ZOD.zod-to-json-schema
: converta seus esquemas Zod em esquemas JSON.@anatine/zod-openapi
: converte um esquema Zod em um SchemaObject
OpenAPI v3.x.zod-fast-check
: Generate fast-check
arbitraries from Zod schemas.zod-dto
: Generate Nest.js DTOs from a Zod schema.fastify-type-provider-zod
: Crie provedores do tipo Fastify a partir de esquemas ZOD.zod-to-openapi
: gerar documentos completos do OpenApi (Swagger) do ZOD, incluindo esquemas, pontos de extremidade e parâmetros.nestjs-graphql-zod
: Generates NestJS GraphQL model classes from Zod schemas. Fornece decoradores de métodos GraphQL que trabalham com esquemas ZOD.zod-openapi
: Crie documentação completa do OpenAPI v3.x a partir de esquemas ZOD.fastify-zod-openapi
: provedor do tipo Fastify, validação, serialização e suporte @fastify/swagger para esquemas ZOD.typeschema
: adaptador universal para validação de esquema.zodex
: (de) serialização para esquemas de zod ts-to-zod
: converta definições de tipascript em esquemas ZOD.@runtyping/zod
: Gere Zod a partir de tipos estáticos e esquema JSON.json-schema-to-zod
: converta seus esquemas JSON em esquemas de Zod. Demoção ao vivo.json-to-zod
: Converta objetos JSON em esquemas ZOD. Demoção ao vivo.graphql-codegen-typescript-validation-schema
: plug-in de gerador de código GraphQL para gerar esquema de validação de formulário a partir do seu esquema GraphQL.zod-prisma
: Generate Zod schemas from your Prisma schema.Supervillain
: Gere esquemas ZOD a partir de suas estruturas de GO.prisma-zod-generator
: Emite esquemas de Zod do seu esquema PRISMA.drizzle-zod
: Emite esquemas de Zod do seu esquema de garoa.prisma-trpc-generator
: Emite roteadores TRPC totalmente implementados e seus esquemas de validação usando Zod.zod-prisma-types
criam tipos de zod a partir de seus modelos prisma.quicktype
: converta objetos JSON e esquemas JSON em esquemas ZOD.@sanity-typed/zod
: Gere esquemas de Zod a partir de esquemas de sanidade.java-to-zod
: converta Pojos em esquemas de ZodOrval
: gerar esquemas de zod de esquemas OpenApiKubb
: Gere SDKs e esquemas Zod a partir de seus esquemas OpenAPI @anatine/zod-mock
: gerar dados simulados de um esquema ZOD. Alimentado por faker.js.zod-mocking
: Gere dados simulados dos seus esquemas ZOD.zod-fixture
: use seus esquemas ZOD para automatizar a geração de acessórios de teste não relevantes de uma maneira determinística.zocker
: Gere dados de mock de seus esquemas plausíveis a partir de seus esquemas.zodock
gera dados simulados com base nos esquemas ZOD.zod-schema-faker
gera dados simulados dos esquemas ZOD. Alimentado por @faker-js/faker e randexp.js freerstore
: Firestore Cost Optimizer.slonik
: Node.js PostGres Client com forte integração do Zod.schemql
: aprimora seu fluxo de trabalho SQL combinando SQL bruto com a validação de segurança e esquema do tipo direcionado.soly
: Crie aplicativos CLI com Zod.pastel
: Crie aplicativos de CLI com reação, zod e tinta.zod-xlsx
: A xlsx based resource validator using Zod schemas.znv
: Type-safe environment parsing and validation for Node.js with Zod schemas.zod-config
: Carregar configurações em várias fontes com adaptadores flexíveis, garantindo a segurança do tipo com Zod.unplugin-environment
: um plug-in para carregar variáveis ambientais com segurança com a validação de esquema, simples com módulo virtual, seguro de tipo com IntelliSense e melhor dx?. Alimentado por zod. zod_utilz
: Utilitários agnósticos da estrutura para Zod.zod-playground
: uma ferramenta para aprender e testar funcionalidades de validação de esquema ZOD. Link.zod-sandbox
: ambiente controlado para testar esquemas ZOD. Demoção ao vivo.zod-dev
: Desativa condicionalmente a análise de tempo de execução do Zod na produção.zod-accelerator
: Acelera a taxa de transferência de Zod até ~ 100x. TypeScript 4.5+!
Você deve ativar o modo strict
em seu tsconfig.json
. Esta é uma prática recomendada para todos os projetos de digitação.
// tsconfig.json
{
// ...
"compilerOptions" : {
// ...
"strict" : true
}
}
npm
(nó/pão) npm install zod # npm
yarn add zod # yarn
bun add zod # bun
pnpm add zod # pnpm
Zod também publica uma versão canária em todos os compromissos. Para instalar o canário:
npm install zod@canary # npm
yarn add zod@canary # yarn
bun add zod@canary # bun
pnpm add zod@canary # pnpm
deno.land/x
(deno)Ao contrário do Node, o Deno conta com as importações diretas de URL em vez de um gerenciador de pacotes como o NPM. Zod está disponível em deno.land/x. A versão mais recente pode ser importada assim:
import { z } from "https://deno.land/x/zod/mod.ts" ;
Você também pode especificar uma versão específica:
import { z } from "https://deno.land/x/[email protected]/mod.ts" ;
O restante deste ReadMe pressupõe que você esteja usando o NPM e importando diretamente do pacote
"zod"
.
Criando um esquema de string simples
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 }
Criando um esquema de objeto
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 ( ) ;
O Zod agora fornece uma maneira mais conveniente de coagir valores primitivos.
const schema = z . coerce . string ( ) ;
schema . parse ( "tuna" ) ; // => "tuna"
schema . parse ( 12 ) ; // => "12"
Durante a etapa de análise, a entrada é passada pela função String()
, que é um JavaScript embutido para coagir dados em strings.
schema . parse ( 12 ) ; // => "12"
schema . parse ( true ) ; // => "true"
schema . parse ( undefined ) ; // => "undefined"
schema . parse ( null ) ; // => "null"
O esquema retornado é uma instância normal ZodString
para que você possa usar todos os métodos de string.
z . coerce . string ( ) . email ( ) . min ( 5 ) ;
Como a coerção funciona
Todos os tipos primitivos suportam a coerção. Zod coerces all inputs using the built-in constructors: 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 - a coerção booleana com z.coerce.boolean()
pode não funcionar como você espera. Qualquer valor verdadeiro é coagido ao true
e qualquer valor falsamente é coagido a 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 obter mais controle sobre a lógica de coerção, considere o uso de z.preprocess
ou z.pipe()
.
Esquemas literais representam um tipo literal, como "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"
Atualmente, não há suporte para literais de data em Zod. Se você tiver um caso de uso para esse recurso, registre um problema.
Zod inclui um punhado de validações específicas de cordas.
// 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 ( ) ;
Confira validator.js para um monte de outras funções úteis de validação de string que podem ser usadas em conjunto com refinamentos.
Você pode personalizar algumas mensagens de erro comuns ao criar um esquema de string.
const name = z . string ( {
required_error : "Name is required" ,
invalid_type_error : "Name must be a string" ,
} ) ;
Ao usar métodos de validação, você pode passar em um argumento adicional para fornecer uma mensagem de erro personalizada.
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 você deve ter notado, o Zod String inclui algumas validações relacionadas a data/hora. Essas validações são baseadas em expressão regular, portanto não são tão rigorosas quanto uma biblioteca de data/hora completa. No entanto, eles são muito convenientes para validar a entrada do usuário.
O método z.string().datetime()
aplica ISO 8601; default is no timezone offsets and arbitrary sub-second decimal precision.
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)
Timezone offsets can be allowed by setting the offset
option to 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)
Você pode restringir adicionalmente a precision
permitida. Por padrão, a precisão arbitrária do subsegundo é suportada (mas 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
Added in Zod 3.23
O método z.string().date()
valida as cadeias no formato AAA 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
Adicionado no Zod 3.23
O método z.string().time()
valida as cadeias no formato HH:MM:SS[.s+]
. O segundo pode incluir precisão decimal arbitrária. Não permite compensações horárias de qualquer 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)
Você pode definir a opção precision
para restringir a precisão 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
O método z.string().ip()
por padrão Validar 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
Você pode definir adicionalmente a 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
Você pode personalizar determinadas mensagens de erro ao criar um esquema numérico.
const age = z . number ( {
required_error : "Age is required" ,
invalid_type_error : "Age must be a number" ,
} ) ;
Zod inclui um punhado de validações específicas de números.
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, você pode passar em um segundo argumento para fornecer uma mensagem de erro personalizada.
z . number ( ) . lte ( 5 , { message : "this?is?too?big" } ) ;
Zod inclui um punhado de validações específicas do 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.
Você pode personalizar determinadas mensagens de erro ao criar um esquema NAN.
const isNaN = z . nan ( {
required_error : "isNaN is required" ,
invalid_type_error : "isNaN must be 'not a number'" ,
} ) ;
Você pode personalizar determinadas mensagens de erro ao criar um esquema booleano.
const isActive = z . boolean ( {
required_error : "isActive is required" ,
invalid_type_error : "isActive must be a boolean" ,
} ) ;
Use Z.Date () para validar instâncias Date
.
z . date ( ) . safeParse ( new Date ( ) ) ; // success: true
z . date ( ) . safeParse ( "2022-01-12T00:00:00.000Z" ) ; // success: false
Você pode personalizar determinadas mensagens de erro ao criar um esquema de data.
const myDateSchema = z . date ( {
required_error : "Please select a date and time" ,
invalid_type_error : "That's not a date!" ,
} ) ;
Zod fornece algumas validações específicas de data.
z . date ( ) . min ( new Date ( "1900-01-01" ) , { message : "Too old" } ) ;
z . date ( ) . max ( new Date ( ) , { message : "Too young!" } ) ;
Coerção até o momento
Desde o zod 3.20, use z.coerce.date()
para passar a entrada através 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 versões ZOD mais antigas, use z.preprocess
como descrito neste thread.
const FishEnum = z . enum ( [ "Salmon" , "Tuna" , "Trout" ] ) ;
type FishEnum = z . infer < typeof FishEnum > ;
// 'Salmon' | 'Tuna' | 'Trout'
z.enum
é uma maneira nativa de zod de declarar um esquema com um conjunto fixo de valores de string permitidos. Passe a matriz de valores diretamente para z.enum()
. Como alternativa, use as const
para definir seus valores de enumeração como uma tupla de cordas. Veja os documentos de afirmação const para obter detalhes.
const VALUES = [ "Salmon" , "Tuna" , "Trout" ] as const ;
const FishEnum = z . enum ( VALUES ) ;
Isso não é permitido, pois Zod não consegue inferir os valores exatos de cada elemento.
const fish = [ "Salmon" , "Tuna" , "Trout" ] ;
const FishEnum = z . enum ( fish ) ;
.enum
Para obter preenchimento automático com um Zod Enum, use a propriedade .enum
do seu esquema:
FishEnum . enum . Salmon ; // => autocompletes
FishEnum . enum ;
/*
=> {
Salmon: "Salmon",
Tuna: "Tuna",
Trout: "Trout",
}
*/
Você também pode recuperar a lista de opções como uma tupla com a propriedade .options
:
FishEnum . options ; // ["Salmon", "Tuna", "Trout"];
.exclude/.extract()
Você pode criar subconjuntos de um ZOD Enum com os métodos .exclude
e .extract
.
const FishEnum = z . enum ( [ "Salmon" , "Tuna" , "Trout" ] ) ;
const SalmonAndTrout = FishEnum . extract ( [ "Salmon" , "Trout" ] ) ;
const TunaOnly = FishEnum . exclude ( [ "Salmon" , "Trout" ] ) ;
As enums de Zod são a abordagem recomendada para definir e validar enumes. Mas se você precisar validar contra uma enumeração de uma biblioteca de terceiros (ou não deseja reescrever suas enumes existentes), poderá usar z.nativeEnum()
.
Enumes 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
Enums de string
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 enums
A função .nativeEnum()
também funciona para objetos as const
.as const
requires 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
You can access the underlying object with the .enum
property:
FruitEnum . enum . Apple ; // "apple"
Você pode tornar qualquer esquema opcional com z.optional()
. This wraps the schema in a ZodOptional
instance and returns the result.
const schema = z . optional ( z . string ( ) ) ;
schema . parse ( undefined ) ; // => returns undefined
type A = z . infer < typeof schema > ; // string | undefined
Por conveniência, você também pode chamar o método .optional()
em um esquema existente.
const user = z . object ( {
username : z . string ( ) . optional ( ) ,
} ) ;
type C = z . infer < typeof user > ; // { username?: string | undefined };
Você pode extrair o esquema embrulhado de uma instância ZodOptional
com .unwrap()
.
const stringSchema = z . string ( ) ;
const optionalString = stringSchema . optional ( ) ;
optionalString . unwrap ( ) === stringSchema ; // true
Da mesma forma, você pode criar tipos anuláveis com z.nullable()
.
const nullableString = z . nullable ( z . string ( ) ) ;
nullableString . parse ( "asdf" ) ; // => "asdf"
nullableString . parse ( null ) ; // => null
Ou use o método .nullable()
.
const E = z . string ( ) . nullable ( ) ; // equivalent to nullableString
type E = z . infer < typeof E > ; // string | null
Extract the inner schema with .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 acessar os esquemas para uma chave específica.
Dog . shape . name ; // => string schema
Dog . shape . age ; // => number schema
.keyof
Use .keyof
para criar um esquema ZodEnum
a partir das chaves de um esquema de objeto.
const keySchema = Dog . keyof ( ) ;
keySchema ; // ZodEnum<["name", "age"]>
.extend
Você pode adicionar campos adicionais a um esquema de objeto com o método .extend
.
const DogWithBreed = Dog . extend ( {
breed : z . string ( ) ,
} ) ;
Você pode usar .extend
para substituir os campos! Cuidado com esse 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 }
Se os dois esquemas compartilharem chaves, as propriedades de B substituem a propriedade de A. O esquema retornado também herda a política "UnknownKeys" (Strip/Strict/Passthrough) e o esquema captador de B.
.pick/.omit
Inspirado nos tipos de utilidade Pick
e Omit
do TypeScript, todos os esquemas de objeto Zod possuem métodos .pick
e .omit
que retornam uma versão modificada. Considere este esquema de receita:
const Recipe = z . object ( {
id : z . string ( ) ,
name : z . string ( ) ,
ingredients : z . array ( z . string ( ) ) ,
} ) ;
Para manter certas chaves, use .pick
.
const JustTheName = Recipe . pick ( { name : true } ) ;
type JustTheName = z . infer < typeof JustTheName > ;
// => { name: string }
Para remover certas teclas, use .omit
.
const NoIDRecipe = Recipe . omit ( { id : true } ) ;
type NoIDRecipe = z . infer < typeof NoIDRecipe > ;
// => { name: string, ingredients: string[] }
.partial
Inspirado no tipo de utilitário tipsScript interno parcial, o método .partial
torna todas as propriedades opcionais.
Começando a partir deste objeto:
const user = z . object ( {
email : z . string ( ) ,
username : z . string ( ) ,
} ) ;
// { email: string; username: string }
Podemos criar uma versão parcial:
const partialUser = user . partial ( ) ;
// { email?: string | undefined; username?: string | undefined }
Você também pode especificar quais propriedades para tornar opcional:
const optionalEmail = user . partial ( {
email : true ,
} ) ;
/*
{
email?: string | undefined;
username: string
}
*/
.deepPartial
O método .partial
é superficial - aplica apenas um nível de profundidade. Há também uma versão "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}[]
}
*/
Limitação importante: os parciais profundos funcionam apenas como esperado nas hierarquias de objetos, matrizes e tuplas.
.required
Ao contrário do método .partial
, o método. .required
torna todas as propriedades necessárias.
Começando a partir deste objeto:
const user = z
. object ( {
email : z . string ( ) ,
username : z . string ( ) ,
} )
. partial ( ) ;
// { email?: string | undefined; username?: string | undefined }
Podemos criar uma versão necessária:
const requiredUser = user . required ( ) ;
// { email: string; username: string }
Você também pode especificar quais propriedades necessárias:
const requiredEmail = user . required ( {
email : true ,
} ) ;
/*
{
email: string;
username?: string | undefined;
}
*/
.passthrough
Por padrão, os esquemas de objeto ZOD tiram as teclas não reconhecidas durante a análise.
const person = z . object ( {
name : z . string ( ) ,
} ) ;
person . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => { name: "bob dylan" }
// extraKey has been stripped
Em vez disso, se você quiser passar por teclas desconhecidas, use .passthrough()
.
person . passthrough ( ) . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => { name: "bob dylan", extraKey: 61 }
.strict
Por padrão, os esquemas de objeto ZOD tiram as teclas não reconhecidas durante a análise. Você pode desaprovar chaves desconhecidas com .strict()
. Se houver alguma chaves desconhecidas na entrada, o Zod lançará um erro.
const person = z
. object ( {
name : z . string ( ) ,
} )
. strict ( ) ;
person . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => throws ZodError
.strip
Você pode usar o método .strip
para redefinir um esquema de objeto para o comportamento padrão (removendo chaves não reconhecidas).
.catchall
Você pode passar um esquema de "captura" em um esquema de objeto. Todas as chaves desconhecidas serão validadas contra isso.
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
O uso de .catchall()
obtém .passthrough()
, .strip()
ou .strict()
. All keys are now considered "known".
const stringArray = z . array ( z . string ( ) ) ;
// equivalent
const stringArray = z . string ( ) . array ( ) ;
Tenha cuidado com o método .array()
. Ele retorna uma nova instância ZodArray
. This means the order in which you call methods matters. Por exemplo:
z . string ( ) . optional ( ) . array ( ) ; // (string | undefined)[]
z . string ( ) . array ( ) . optional ( ) ; // string[] | undefined
.element
Use .element
para acessar o esquema para um elemento da matriz.
stringArray . element ; // => string schema
.nonempty
Se você deseja garantir que uma matriz contenha pelo menos um 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, você pode especificar uma mensagem de erro personalizada:
// 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
Ao contrário .nonempty()
esses métodos não alteram o tipo inferido.
Ao contrário das matrizes, as tuplas têm um número fixo de elementos e cada elemento pode ter um 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 }]
A variadic ("rest") argument can be added with the .rest
method.
const variadicTuple = z . tuple ( [ z . string ( ) ] ) . rest ( z . number ( ) ) ;
const result = variadicTuple . parse ( [ "hello" , 1 , 2 , 3 ] ) ;
// => [string, ...number[]];
Zod inclui um método z.union
integrado para compor "ou" tipos.
const stringOrNumber = z . union ( [ z . string ( ) , z . number ( ) ] ) ;
stringOrNumber . parse ( "foo" ) ; // passes
stringOrNumber . parse ( 14 ) ; // passes
Zod testará a entrada em relação a cada uma das "opções" em ordem e retornará o primeiro valor que valida com sucesso.
Por conveniência, você também pode usar o método .or
:
const stringOrNumber = z . string ( ) . or ( z . number ( ) ) ;
Validação opcional da string:
Para validar uma entrada de formulário opcional, você pode união a validação de string desejada com uma string vazia literal.
Este exemplo valida uma entrada opcional, mas precisa conter um URL válido:
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
Uma união discriminada é uma união de esquemas de objetos que todos compartilham uma chave específica.
type MyUnion =
| { status : "success" ; data : string }
| { status : "failed" ; error : Error } ;
Tais sindicatos podem ser representados com o método z.discriminatedUnion
. Isso permite uma avaliação mais rápida, porque o Zod pode verificar a chave do discriminador ( status
no exemplo acima) para determinar qual esquema deve ser usado para analisar a entrada. Isso torna a análise mais eficiente e permite que o Zod relate erros mais amigáveis.
Com o método básico da união, a entrada é testada em relação a cada uma das "opções" fornecidas e, no caso de invalidez, os problemas de todas as "opções" são mostrados no erro ZOD. Por outro lado, o sindicato discriminado permite selecionar apenas uma das "opções", testar contra ele e mostrar apenas os problemas relacionados a essa "opção".
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" } ) ;
Você pode extrair uma referência à matriz de esquemas com a propriedade .options
.
myUnion . options ; // [ZodObject<...>, ZodObject<...>]
Para mesclar dois ou mais sindicatos discriminados, use .options
com destruição.
const A = z . discriminatedUnion ( "status" , [
/* options */
] ) ;
const B = z . discriminatedUnion ( "status" , [
/* options */
] ) ;
const AB = z . discriminatedUnion ( "status" , [ ... A . options , ... B . options ] ) ;
Esquemas de registro são usados para validar tipos como Record<string, number>
. Isso é particularmente útil para armazenar ou armazenar em cache por 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 }>
O esquema e o tipo inferido podem ser usados assim:
const userStore : UserStore = { } ;
userStore [ "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd" ] = {
name : "Carlotta" ,
} ; // passes
userStore [ "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd" ] = {
whatever : "Ice cream sundae" ,
} ; // TypeError
Uma nota sobre chaves numéricas
While z.record(keyType, valueType)
is able to accept numerical key types and TypeScript's built-in Record type is Record<KeyType, ValueType>
, it's hard to represent the TypeScript type Record<number, any>
in Zod.
Como se vê, o comportamento do TypeScript em torno de [k: number]
é um pouco inintivo:
const testMap : { [ k : number ] : string } = {
1 : "one" ,
} ;
for ( const key in testMap ) {
console . log ( ` ${ key } : ${ typeof key } ` ) ;
}
// prints: `1: string`
Como você pode ver, o JavaScript lança automaticamente todas as teclas de objeto para strings sob o capô. Como o Zod está tentando preencher a lacuna entre os tipos estáticos e de tempo de execução, não faz sentido fornecer uma maneira de criar um esquema de registro com teclas numéricas, pois não existe uma chave numérica no JavaScript de tempo de execução.
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>
Esquemas de configuração podem ser restringidos ainda mais com os seguintes métodos de utilidade.
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
Interseções são úteis para criar tipos "lógicos e". Isso é útil para cruzar dois 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 ) ;
Embora em muitos casos, é recomendável usar A.merge(B)
para mesclar dois objetos. O método .merge
retorna uma nova instância ZodObject
, enquanto A.and(B)
retorna uma instância ZodIntersection
menos útil que não possui métodos de objeto comuns, como pick
e 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
Você pode definir um esquema recursivo em Zod, mas, devido a uma limitação do texto datilografado, seu tipo não pode ser deduzido estaticamente. Instead you'll need to define the type definition manually, and provide it to Zod as a "type hint".
const baseCategorySchema = z . object ( {
name : z . string ( ) ,
} ) ;
type Category = z . infer < typeof baseCategorySchema > & {
subcategories : Category [ ] ;
} ;
const categorySchema : z . ZodType < Category > = baseCategorySchema . extend ( {
subcategories : z . lazy ( ( ) => categorySchema . array ( ) ) ,
} ) ;
categorySchema . parse ( {
name : "People" ,
subcategories : [
{
name : "Politicians" ,
subcategories : [
{
name : "Presidents" ,
subcategories : [ ] ,
} ,
] ,
} ,
] ,
} ) ; // passes
Thanks to crasite for this example.
Ao usar z.ZodType
com z.ZodEffects
( .refine
, .transform
, preprocess
, etc ...), você precisará definir os tipos de entrada e saída do 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 ( ) ) ,
} ) ;
Thanks to marcus13371337 and JoelBeeldi for this example.
Se você deseja validar qualquer valor JSON, poderá usar o snippet abaixo.
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 ) ;
Obrigado a Ggoodman por sugerir isso.
Apesar de apoiar esquemas recursivos, a passagem de dados cíclicos para Zod causará um loop infinito em alguns casos.
Para detectar objetos cíclicos antes que eles causem problemas, considere essa abordagem.
const numberPromise = z . promise ( z . number ( ) ) ;
"Parsing" funciona de maneira um pouco diferente com os esquemas de promessa. A validação acontece em duas partes:
.then
e .catch
.)..then
para anexar uma etapa de validação adicional à promessa existente. Você precisará usar .catch
na promessa devolvida para lidar com falhas de validação. 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
} ;
Você pode usar z.instanceof
para verificar se a entrada é uma instância de uma classe. Isso é útil para validar insumos contra classes exportadas de bibliotecas de terceiros.
class Test {
name : string ;
}
const TestSchema = z . instanceof ( Test ) ;
const blob : any = "whatever" ;
TestSchema . parse ( new Test ( ) ) ; // passes
TestSchema . parse ( blob ) ; // throws
Zod também permite definir "esquemas de função". Isso facilita a validação das entradas e saídas de uma função sem misturar seu código de validação e "lógica de negócios".
Você pode criar um esquema de função com z.function(args, returnType)
.
const myFunction = z . function ( ) ;
type myFunction = z . infer < typeof myFunction > ;
// => ()=>unknown
Defina entradas e saídas.
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
Os esquemas de função possuem um método .implement()
que aceita uma função e retorna uma nova função que valida automaticamente suas entradas e saídas.
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
Se você se preocupa apenas com a validação de entradas, não ligue para o método .returns()
. O tipo de saída será inferido da implementação.
Você pode usar a opção
z.void()
especial se sua função não retornar nada. Isso permitirá que o Zod inferirá corretamente o tipo de funções de retorno de vazios. (As funções de retorno de vazio realmente retornam indefinidas.)
const myFunction = z
. function ( )
. args ( z . string ( ) )
. implement ( ( arg ) => {
return [ arg . length ] ;
} ) ;
myFunction ; // (arg: string)=>number[]
Extraia os esquemas de entrada e saída de um esquema de função.
myFunction . parameters ( ) ;
// => ZodTuple<[ZodString, ZodNumber]>
myFunction . returnType ( ) ;
// => ZodBoolean
O Zod agora suporta coerção primitiva sem a necessidade de
.preprocess()
. Veja o coerção docs para obter mais informações.
Normalmente, o Zod opera sob um paradigma "parse e transforma". Zod valida a entrada primeiro e depois passa por uma cadeia de funções de transformação. (Para obter mais informações sobre transformações, leia os documentos .Transform.)
Mas às vezes você deseja aplicar alguma transformação à entrada antes da análise. Um caso de uso comum: coerção de tipo. Zod permite isso com o z.preprocess()
.
const castToString = z . preprocess ( ( val ) => String ( val ) , z . string ( ) ) ;
Isso retorna uma instância ZodEffects
. ZodEffects
é uma classe de invólucro que contém toda a lógica referente ao pré -processamento, refinamentos e transformações.
You can create a Zod schema for any TypeScript type by using z.custom()
. Isso é útil para criar esquemas para tipos que não são suportados pelo Zod Out of the Box, como literais de string de modelo.
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;
Se você não fornecer uma função de validação, o Zod permitirá qualquer valor. This can be dangerous!
z . custom < { arg : string } > ( ) ; // performs no validation
Você pode personalizar a mensagem de erro e outras opções passando um segundo argumento. Este parâmetro funciona da mesma maneira que o parâmetro params de .refine
.
z . custom < ... > ( ( val ) => ... , "custom error message" ) ;
Todos os esquemas ZOD contêm certos métodos.
.parse
.parse(data: unknown): T
Dado qualquer esquema ZOD, você pode chamar seu método .parse
para verificar data
é válido. Se for, um valor é retornado com informações de tipo completo! Otherwise, an error is thrown.
IMPORTANTE: O valor retornado por
.parse
é um clone profundo da variável que você passou.
const stringSchema = z . string ( ) ;
stringSchema . parse ( "fish" ) ; // => returns "fish"
stringSchema . parse ( 12 ) ; // throws error
.parseAsync
.parseAsync(data:unknown): Promise<T>
Se você usar refinamentos ou transformados assíncronos (mais sobre eles mais tarde), precisará 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; }
Se você não deseja que o Zod ligue erros quando a validação falhar, use .safeParse
. Este método retorna um objeto que contém os dados analisados com sucesso ou uma instância do Zoderror contendo informações detalhadas sobre os problemas de validação.
stringSchema . safeParse ( 12 ) ;
// => { success: false; error: ZodError }
stringSchema . safeParse ( "billie" ) ;
// => { success: true; data: 'billie' }
O resultado é um sindicato discriminado , para que você possa lidar com erros de maneira muito conveniente:
const result = stringSchema . safeParse ( "billie" ) ;
if ( ! result . success ) {
// handle error then return
result . error ;
} else {
// do something
result . data ;
}
.safeParseAsync
Alias:
.spa
Uma versão assíncrona do safeParse
.
await stringSchema . safeParseAsync ( "billie" ) ;
Por conveniência, isso foi alias para .spa
:
await stringSchema . spa ( "billie" ) ;
.refine
.refine(validator: (data:T)=>any, params?: RefineParams)
O Zod permite fornecer lógica de validação personalizada por meio de refinamentos . (Para recursos avançados, como criar vários problemas e personalizar códigos de erro, consulte .superRefine
.)
Zod foi projetado para refletir o TypeScript o mais próximo possível. Mas existem muitos chamados "tipos de refinamento" que você pode verificar que não pode ser representado no sistema de tipo do TypeScript. Por exemplo: verificar se um número é um número inteiro ou que uma string é um endereço de email válido.
Por exemplo, você pode definir uma verificação de validação personalizada em qualquer esquema ZOD com .refine
:
const myString = z . string ( ) . refine ( ( val ) => val . length <= 255 , {
message : "String can't be more than 255 characters" ,
} ) ;
️ As funções de refinamento não devem jogar. Em vez disso, eles devem retornar um valor falsamente para sinalizar a falha.
Como você pode ver,. .refine
leva dois argumentos.
T
- o tipo inferido do esquema) e retorna any
. Qualquer valor verdadeiro passará a validação. (Antes de [email protected], a função de validação teve que devolver um 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 avançados, o segundo argumento também pode ser uma função que retorna 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" } ) ;
Como você forneceu um parâmetro path
, o erro resultante será:
ZodError {
issues : [ {
"code" : "custom" ,
"path" : [ "confirm" ] ,
"message" : "Passwords don't match"
} ]
}
Os refinamentos também podem ser assíncronos:
const userId = z . string ( ) . refine ( async ( id ) => {
// verify that ID exists in database
return true ;
} ) ;
️ Se você usar refinamentos assíncronos, deverá usar o método.parseAsync
para analisar dados! Otherwise Zod will throw an error.
Transformações e refinamentos podem ser intercalados:
z . string ( )
. transform ( ( val ) => val . length )
. refine ( ( val ) => val > 25 ) ;
.superRefine
O método .refine
é na verdade o açúcar sintático no topo de um método mais versátil (e verboso) chamado superRefine
. Aqui está um exemplo:
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.` ,
} ) ;
}
} ) ;
You can add as many issues as you like. Se ctx.addIssue
não for chamado durante a execução da função, a validação passa.
Normalmente, os refinamentos sempre criam problemas com um código de erro ZodIssueCode.custom
, mas com superRefine
é possível lançar questões de qualquer ZodIssueCode
. Cada código de emissão é descrito em detalhes no guia de manuseio de erros: error_handling.md.
Por padrão, a análise continuará mesmo após a falha de uma verificação de refinamento. Por exemplo, se você unir vários refinamentos, todos serão executados. However, it may be desirable to abort early to prevent later refinements from being executed. Para conseguir isso, passe a bandeira fatal
para ctx.addIssue
e retorne 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" ,
} ) ;
}
} ) ;
Se você fornecer um predicado de tipo para .refine()
ou .superRefine()
, o tipo resultante será reduzido ao tipo do seu predicado. Isso é útil se você estiver misturando vários refinamentos e transformações acorrentados:
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`!" ) ;
️ Você deve usarctx.addIssue()
em vez de retornar um valor booleano para indicar se a validação passa. Sectx.addIssue
não for chamado durante a execução da função, a validação passa.
.transform
Para transformar dados após a análise, use o método transform
.
const stringToNumber = z . string ( ) . transform ( ( val ) => val . length ) ;
stringToNumber . parse ( "string" ) ; // => 6
Observe que stringToNumber
acima é uma instância da subclasse ZodEffects
. Não é uma instância de ZodString
. If you want to use the built-in methods of ZodString
(eg .email()
) you must apply those methods before any transforms.
const emailToDomain = z
. string ( )
. email ( )
. transform ( ( val ) => val . split ( "@" ) [ 1 ] ) ;
emailToDomain . parse ( "[email protected]" ) ; // => example.com
O método .transform
pode validar e transformar simultaneamente o valor. Isso geralmente é mais simples e menos duplicado do que transform
e refine
.
As with .superRefine
, the transform function receives a ctx
object with an addIssue
method that can be used to register validation issues.
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 ;
} ) ;
Transformações e refinamentos podem ser intercalados. Estes serão executados na ordem em que são declarados.
const nameToGreeting = z
. string ( )
. transform ( ( val ) => val . toUpperCase ( ) )
. refine ( ( val ) => val . length > 15 )
. transform ( ( val ) => `Hello ${ val } ` )
. refine ( ( val ) => val . indexOf ( "!" ) === - 1 ) ;
Transformações também podem ser assíncronas.
const IdToUser = z
. string ( )
. uuid ( )
. transform ( async ( id ) => {
return await getUserById ( id ) ;
} ) ;
️ Se o seu esquema contiver transformações assíncronas, você deverá usar .parseasync () ou .SafeparsEasync () para analisar dados. Caso contrário, Zod lançará um erro.
.default
Você pode usar transformações para implementar o conceito de "valores padrão" no Zod.
const stringWithDefault = z . string ( ) . default ( "tuna" ) ;
stringWithDefault . parse ( undefined ) ; // => "tuna"
Opcionalmente, você pode passar uma função para .default
que será reexecutada sempre que um valor padrão precisar ser gerado:
const numberWithRandomDefault = z . number ( ) . default ( Math . random ) ;
numberWithRandomDefault . parse ( undefined ) ; // => 0.4413456736055323
numberWithRandomDefault . parse ( undefined ) ; // => 0.1871840107401901
numberWithRandomDefault . parse ( undefined ) ; // => 0.7223408162401552
Conceitualmente, é assim que o Zod processa os valores padrão:
undefined
, the default value is returned.describe
Use .describe()
para adicionar uma propriedade description
ao 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…
Isso pode ser útil para documentar um campo, por exemplo, em um esquema JSON usando uma biblioteca como zod-to-json-schema
).
.catch
Use .catch()
to provide a "catch value" to be returned in the event of a parsing error.
const numberWithCatch = z . number ( ) . catch ( 42 ) ;
numberWithCatch . parse ( 5 ) ; // => 5
numberWithCatch . parse ( "tuna" ) ; // => 42
Opcionalmente, você pode passar uma função para .catch
que será reexecionado sempre que um valor padrão precisar ser gerado. Um objeto ctx
que contém o erro capturado será passado para esta função.
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
Conceitualmente, é assim que Zod processa "Catch Values":
.optional
Um método de conveniência que retorna uma versão opcional de um esquema.
const optionalString = z . string ( ) . optional ( ) ; // string | undefined
// equivalent to
z . optional ( z . string ( ) ) ;
.nullable
Um método de conveniência que retorna uma versão anulável de um esquema.
const nullableString = z . string ( ) . nullable ( ) ; // string | null
// equivalent to
z . nullable ( z . string ( ) ) ;
.nullish
Um método de conveniência que retorna uma versão "nula" de um esquema. Esquemas nulos aceitarão undefined
e null
. Leia mais sobre o conceito de "Nullish" nas Notas de liberação do TypeScript 3.7.
const nullishString = z . string ( ) . nullish ( ) ; // string | null | undefined
// equivalent to
z . string ( ) . nullable ( ) . optional ( ) ;
.array
Um método de conveniência que retorna um esquema de matriz para o tipo fornecido:
const stringArray = z . string ( ) . array ( ) ; // string[]
// equivalent to
z . array ( z . string ( ) ) ;
.promise
Um método de conveniência para os tipos de promessa:
const stringPromise = z . string ( ) . promise ( ) ; // Promise<string>
// equivalent to
z . promise ( z . string ( ) ) ;
.or
Um método de conveniência para tipos de sindicatos.
const stringOrNumber = z . string ( ) . or ( z . number ( ) ) ; // string | number
// equivalent to
z . union ( [ z . string ( ) , z . number ( ) ] ) ;
.and
Um método de conveniência para criar tipos de interseção.
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>
O sistema de tipo do TypeScript é estrutural, o que significa que quaisquer dois tipos estruturalmente equivalentes são considerados iguais.
type Cat = { name : string } ;
type Dog = { name : string } ;
const petCat = ( cat : Cat ) => { } ;
const fido : Dog = { name : "fido" } ;
petCat ( fido ) ; // works fine
Em alguns casos, pode ser desejável simular a digitação nominal dentro do datilografript. Por exemplo, você pode escrever uma função que aceite apenas uma entrada que foi validada pelo Zod. Isso pode ser alcançado com tipos de marca (também conhecidos 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" } ) ;
Sob o capô, isso funciona anexando uma "marca" ao tipo inferido usando um tipo de interseção. Dessa forma, estruturas de dados simples/sem marca não são mais atribuíveis ao tipo inferido do esquema.
const Cat = z . object ( { name : z . string ( ) } ) . brand < "Cat" > ( ) ;
type Cat = z . infer < typeof Cat > ;
// {name: string} & {[symbol]: "Cat"}
Observe que os tipos de marca não afetam o resultado do tempo de execução .parse
. É uma construção apenas estática.
.readonly
.readonly() => ZodReadonly<this>
Este método retorna uma instância de esquema ZodReadonly
que analisa a entrada usando o esquema base e chama Object.freeze()
no resultado. O tipo inferido também é 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
O tipo inferido usa os tipos de leitura internos do TypeScript quando relevantes.
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
Esquemas podem ser acorrentados à validação "pipelines". É útil para validar facilmente o resultado após um .transform()
:
z . string ( )
. transform ( ( val ) => val . length )
. pipe ( z . number ( ) . min ( 5 ) ) ;
O método .pipe()
retorna uma instância ZodPipeline
.
.pipe()
para corrigir problemas comuns com z.coerce
. Você pode restringir a entrada aos tipos que funcionam bem com a coerção escolhida. Em seguida, use .pipe()
para aplicar a coerção.
sem entrada restrita:
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
com entrada restrita:
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
Você também pode usar essa técnica para evitar coercões que lançam erros não capturados.
sem entrada restrita:
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
com entrada restrita:
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
E quanto a transformações?
Na realidade, cada esquema ZOD rastreia internamente dois tipos: uma entrada e uma saída. For most schemas (eg z.string()
) these two are the same. Mas uma vez que você adiciona transformações à mistura, esses dois valores podem divergir. Por exemplo z.string().transform(val => val.length)
possui uma entrada de string
e uma saída de number
.
Você pode extrair separadamente os tipos de entrada e saída como assim:
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
Com os genéricos do TypeScript, você pode escrever funções reutilizáveis que aceitam os esquemas do ZOD como parâmetros. This enables you to create custom validation logic, schema transformations, and more, while maintaining type safety and inference.
Ao tentar escrever uma função que aceite um esquema ZOD como uma entrada, é tentador tentar algo assim:
function inferSchema < T > ( schema : z . ZodType < T > ) {
return schema ;
}
Essa abordagem está incorreta e limita a capacidade do TypeScript de inferir adequadamente o argumento. Não importa o que você passe, o tipo de schema
será uma instância do 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