Notas de la versión
Después de un año de desarrollo activo: ¡Zod 4 ya es estable! Es más rápido, más ligero, más eficiente con tsc e implementa algunas características solicitadas desde hace mucho tiempo.
Muchas gracias a Clerk, quien apoyó mi trabajo en Zod 4 a través de su extremadamente generosa OSS Fellowship. Fueron un socio increíble durante todo el proceso de desarrollo (¡mucho más largo de lo previsto!).
Versionado
Para actualizar:
Para obtener una lista completa de cambios rupturistas (breaking changes), consulta la Guía de migración. Esta publicación se centra en las nuevas características y mejoras.
¿Por qué una nueva versión mayor?
Zod v3.0 fue lanzado en mayo de 2021 (!). En ese entonces, Zod tenía 2700 estrellas en GitHub y 600k descargas semanales. Hoy tiene 37.8k estrellas y 31M de descargas semanales (¡frente a los 23M cuando salió la beta hace 6 semanas!). Después de 24 versiones menores, el código base de Zod 3 había alcanzado un techo; las características y mejoras más solicitadas requieren cambios rupturistas.
Zod 4 soluciona una serie de limitaciones de diseño de larga data de Zod 3 de un solo golpe, allanando el camino para varias características solicitadas desde hace mucho tiempo y un gran salto en el rendimiento. Cierra 9 de los 10 problemas abiertos más votados de Zod. Con suerte, servirá como la nueva base para muchos años más por venir.
Para un desglose escaneable de lo que es nuevo, consulta la tabla de contenido. Haz clic en cualquier elemento para saltar a esa sección.
Pruebas de rendimiento (Benchmarks)
Puedes ejecutar estas pruebas tú mismo en el repositorio de Zod:
Luego, para ejecutar una prueba en particular:
Análisis de cadenas 14x más rápido
Análisis de matrices 7x más rápido
Análisis de objetos 6.5x más rápido
Esto ejecuta el benchmark de bibliotecas de validación Moltar.
Reducción de 100x en instanciaciones de tsc
Considera el siguiente archivo simple:
Compilar este archivo con tsc --extendedDiagnostics usando "zod/v3" resulta en >25000 instanciaciones de tipos. Con "zod/v4" solo resulta en ~175.
El repositorio de Zod contiene un patio de juegos de benchmarking de tsc. Pruébalo tú mismo usando los benchmarks del compilador en packages/tsc. Los números exactos pueden cambiar a medida que evoluciona la implementación.
Más importante aún, Zod 4 ha rediseñado y simplificado los genéricos de ZodObject y otras clases de esquema para evitar algunas "explosiones de instanciación" perniciosas. Por ejemplo, encadenar .extend() y .omit() repetidamente, algo que anteriormente causaba problemas al compilador:
En Zod 3, esto tomaba 4000ms en compilar; y agregar llamadas adicionales a .extend() desencadenaría un error de "Posiblemente infinito". En Zod 4, esto compila en 400ms, 10x más rápido.
Junto con el próximo compilador tsgo, el rendimiento del editor de Zod 4 escalará a esquemas y bases de código mucho más grandes.
Reducción de 2x en el tamaño del paquete central
Considera el siguiente script simple.
Es tan simple como parece cuando se trata de validación. Eso es intencional; es una buena manera de medir el tamaño del paquete central—el código que terminará en el paquete incluso en casos simples. Empaquetaremos esto con rollup usando tanto Zod 3 como Zod 4 y compararemos los paquetes finales.
| Paquete | Paquete (gzip) |
|---|---|
| Zod 3 | 12.47kb |
| Zod 4 | 5.36kb |
El paquete central es ~57% más pequeño en Zod 4 (2.3x). ¡Eso es bueno! Pero podemos hacerlo mucho mejor.
Presentando Zod Mini
La API de Zod, cargada de métodos, es fundamentalmente difícil de someter a tree-shaking. Incluso nuestro script simple z.boolean() extrae las implementaciones de un montón de métodos que no usamos, como .optional(), .array(), etc. Escribir implementaciones más ligeras solo te lleva hasta cierto punto. Ahí es donde entra Zod Mini.
Es una variante de Zod con una API funcional y apta para tree-shaking que se corresponde uno a uno con zod. Donde Zod usa métodos, Zod Mini generalmente usa funciones envolventes:
¡No todos los métodos han desaparecido! Los métodos de análisis (parsing) son idénticos en Zod y Zod Mini:
También hay un método .check() de propósito general utilizado para agregar refinamientos.
Los siguientes refinamientos de nivel superior están disponibles en Zod Mini. Debería ser bastante autoexplicativo a qué métodos de Zod corresponden.
Esta API más funcional hace que sea más fácil para los empaquetadores eliminar las API que no utilizas (tree-shaking). Si bien Zod regular se sigue recomendando para la mayoría de los casos de uso, cualquier proyecto con restricciones de tamaño de paquete poco comunes debería considerar Zod Mini.
Reducción de 6.6x en el tamaño del paquete central
Aquí está el script de arriba, actualizado para usar "zod/mini" en lugar de "zod".
Cuando empaquetamos esto con rollup, el tamaño del paquete gzippeado es 1.88kb. Esa es una reducción del 85% (6.6x) en el tamaño del paquete central en comparación con zod@3.
| Paquete | Paquete (gzip) |
|---|---|
| Zod 3 | 12.47kb |
| Zod 4 (regular) | 5.36kb |
| Zod 4 (mini) | 1.88kb |
Obtén más información en la página de documentación dedicada a zod/mini. Los detalles completos de la API se mezclan en las páginas de documentación existentes; los bloques de código contienen pestañas separadas para "Zod" y "Zod Mini" donde sus API divergen.
Metadatos
Zod 4 introduce un nuevo sistema para agregar metadatos fuertemente tipados a tus esquemas. Los metadatos no se almacenan dentro del esquema en sí; en su lugar, se almacenan en un "registro de esquemas" que asocia un esquema con algunos metadatos tipados. Para crear un registro con z.registry():
Para agregar esquemas a tu registro:
Alternativamente, puedes usar el método .register() en un esquema para mayor comodidad:
El registro global
Zod también exporta un registro global z.globalRegistry que acepta algunos metadatos comunes compatibles con JSON Schema:
.meta()
Para agregar convenientemente un esquema a z.globalRegistry, usa el método .meta().
Para compatibilidad con Zod 3, .describe() todavía está disponible, pero se prefiere .meta().
Conversión a JSON Schema
Zod 4 introduce la conversión a JSON Schema de primera parte a través de z.toJSONSchema().
Cualquier metadato en z.globalRegistry se incluye automáticamente en la salida de JSON Schema.
Consulta la documentación de JSON Schema para obtener información sobre cómo personalizar el JSON Schema generado.
Objetos recursivos
Este fue inesperado. Después de años de tratar de resolver este problema, finalmente encontré una manera de inferir correctamente los tipos de objetos recursivos en Zod. Para definir un tipo recursivo:
También puedes representar tipos mutuamente recursivos:
A diferencia del patrón de Zod 3 para tipos recursivos, no se requiere casting de tipos. Los esquemas resultantes son instancias simples de ZodObject y tienen el conjunto completo de métodos disponibles.
Esquemas de archivo (File schemas)
Para validar instancias de File:
Internacionalización
Zod 4 introduce una nueva API locales para traducir globalmente los mensajes de error a diferentes idiomas.
Consulta la lista completa de configuraciones regionales admitidas en Personalización de errores; esta sección siempre se actualiza con una lista de idiomas admitidos a medida que están disponibles.
Impresión bonita de errores
La popularidad del paquete zod-validation-error demuestra que existe una demanda significativa de una API oficial para la impresión bonita de errores. Si estás utilizando ese paquete actualmente, por supuesto, continúa usándolo.
Zod ahora implementa una función de nivel superior z.prettifyError para convertir un ZodError en una cadena formateada fácil de usar.
Esto devuelve la siguiente cadena multilínea imprimible bonita:
Actualmente el formato no es configurable; esto puede cambiar en el futuro.
Formatos de cadena de nivel superior
Todos los "formatos de cadena" (correo electrónico, etc.) han sido promovidos a funciones de nivel superior en el módulo z. Esto es a la vez más conciso y más apto para tree-shaking. Los equivalentes de método (z.string().email(), etc.) todavía están disponibles pero han quedado obsoletos. Se eliminarán en la próxima versión mayor.
Regex de correo electrónico personalizado
La API z.email() ahora admite una expresión regular personalizada. No hay una única expresión regular de correo electrónico canónica; diferentes aplicaciones pueden optar por ser más o menos estrictas. Para mayor comodidad, Zod exporta algunas comunes.
Tipos de literal de plantilla (Template literal types)
Zod 4 implementa z.templateLiteral(). Los tipos de literal de plantilla son quizás la característica más grande del sistema de tipos de TypeScript que no era representable anteriormente.
Cada tipo de esquema de Zod que se puede convertir en cadena almacena una regex interna: cadenas, formatos de cadena como z.email(), números, booleanos, bigint, enumeraciones, literales, indefinido/opcional, nulo/anulable y otros literales de plantilla. El constructor z.templateLiteral concatena estos en una súper-regex, por lo que cosas como los formatos de cadena (z.email()) se aplican correctamente (¡pero los refinamientos personalizados no!).
Lee la documentación de literales de plantilla para más información.
Formatos numéricos (Number formats)
Se han agregado nuevos "formatos" numéricos para representar tipos de enteros y flotantes de ancho fijo. Estos devuelven una instancia de ZodNumber con las restricciones mínimas/máximas adecuadas ya agregadas.
Del mismo modo, también se han agregado los siguientes formatos numéricos bigint. Estos tipos de enteros exceden lo que se puede representar de forma segura mediante un number en JavaScript, por lo que devuelven una instancia de ZodBigInt con las restricciones mínimas/máximas adecuadas ya agregadas.
Stringbool
La API existente z.coerce.boolean() es muy simple: los valores falsy (false, undefined, null, 0, "", NaN, etc.) se vuelven false, los valores truthy se vuelven true.
Esta sigue siendo una buena API, y su comportamiento se alinea con las otras API z.coerce. Pero algunos usuarios solicitaron una coerción booleana más sofisticada al estilo "env". Para soportar esto, Zod 4 introduce z.stringbool():
Para personalizar los valores truthy y falsy:
Consulta la documentación de z.stringbool() para obtener más información.
Personalización de errores simplificada
La mayoría de los cambios rupturistas en Zod 4 implican las API de personalización de errores. Eran un poco desordenadas en Zod 3; Zod 4 hace las cosas significativamente más elegantes, hasta el punto en que creo que vale la pena destacarlo aquí.
En resumen, ahora hay un único parámetro unificado error para personalizar errores, reemplazando las siguientes API:
Reemplaza message con error. (El parámetro message todavía es compatible pero está obsoleto).
Reemplaza invalid_type_error y required_error con error (sintaxis de función):
Reemplaza errorMap con error (sintaxis de función):
z.discriminatedUnion() mejorado
Las uniones discriminadas ahora admiten una serie de tipos de esquema que no se admitían anteriormente, incluidas uniones y tuberías (pipes):
Quizás lo más importante es que las uniones discriminadas ahora se componen: puedes usar una unión discriminada como miembro de otra.
Múltiples valores en z.literal()
La API z.literal() ahora admite opcionalmente múltiples valores.
Los refinamientos viven dentro de los esquemas
En Zod 3, se almacenaban en una clase ZodEffects que envolvía el esquema original. Esto era inconveniente, ya que significaba que no podías intercalar .refine() con otros métodos de esquema como .min().
En Zod 4, los refinamientos se almacenan dentro de los esquemas mismos, por lo que el código anterior funciona como se espera.
.overwrite()
El método .transform() es extremadamente útil, pero tiene una gran desventaja: el tipo de salida ya no es introspectable en tiempo de ejecución. La función de transformación es una caja negra que puede devolver cualquier cosa. Esto significa (entre otras cosas) que no hay una forma segura de convertir el esquema a JSON Schema.
Zod 4 introduce un nuevo método .overwrite() para representar transformaciones que no cambian el tipo inferido. A diferencia de .transform(), este método devuelve una instancia de la clase original. La función de sobrescritura se almacena como un refinamiento, por lo que no modifíca (y no puede modificar) el tipo inferido.
Los métodos existentes .trim(), .toLowerCase() y .toUpperCase() se han reimplementado usando .overwrite().
Una base extensible: zod/v4/core
Si bien esto no será relevante para la mayoría de los usuarios de Zod, vale la pena destacarlo. La adición de Zod Mini requirió la creación de un subpaquete compartido zod/v4/core que contiene la funcionalidad central compartida entre Zod y Zod Mini.
Al principio me resistí a esto, pero ahora lo veo como una de las características más importantes de Zod 4. Permite a Zod subir de nivel de una biblioteca simple a un "sustrato" de validación rápido que se puede espolvorear en otras bibliotecas.
Si estás creando una biblioteca de esquemas, consulta las implementaciones de Zod y Zod Mini para ver cómo construir sobre la base que proporciona zod/v4/core. No dudes en ponerte en contacto en las discusiones de GitHub o a través de X/Bluesky para obtener ayuda o comentarios.
Conclusión
Planeo escribir una serie de publicaciones adicionales explicando el proceso de diseño detrás de algunas características importantes como Zod Mini. Actualizaré esta sección a medida que se publiquen.
Para los autores de bibliotecas, ahora hay una guía dedicada Para autores de bibliotecas que describe las mejores prácticas para construir sobre Zod. Responde preguntas comunes sobre cómo soportar Zod 3 y Zod 4 (incluido Mini) simultáneamente.
¡Feliz análisis (parsing)!
— Colin McDonnell @colinhacks

