ArkType, una alternativa más rápida a Zod
Compara la sintaxis de ArkType y Zod, la inferencia de tipos, la velocidad de validación y el manejo de respuestas API. Decide cuándo usar ArkType o seguir con Zod.
ArkType es un validador en tiempo de ejecución para TypeScript que define los esquemas como cadenas con una sintaxis similar a la de TypeScript y los compila en validadores optimizados. Para un equipo con una base de código estable en Zod, rara vez compensa una migración completa. Para un proyecto nuevo o una ruta de validación crítica (hot path), vale la pena probarlo.
Si usas Zod, probablemente hayas visto la gráfica de benchmarks de ArkType y te hayas preguntado si la velocidad justifica aprender una sintaxis nueva.
Este artículo traslada un único esquema User de Zod a ArkType. Aborda la inferencia de tipos, el rendimiento, la validación de la respuesta de una API y las contrapartidas, y luego indica claramente cuándo conviene quedarse con Zod. Los ejemplos usan ArkType 2.2 y Zod 4.
Puntos clave
- ArkType define los esquemas como cadenas similares a TypeScript, de modo que
"'android' | 'ios'"se lee exactamente igual que el tipo unión que genera, mientras que en Zod se escribez.enum(["android", "ios"]). - Al invocar un tipo de ArkType sobre datos desconocidos, se obtiene el valor validado o una instancia de
ArkErrors, por lo que la comprobación idiomática esout instanceof type.errors. - La página principal de ArkType afirma que es 20 veces más rápido que Zod 4 en tiempo de ejecución. Es una cifra del propio proveedor, y desde entonces Zod 4.5 ha incorporado
z.compile()para las rutas críticas. - ArkType 2.2 acepta cualquier validador compatible con Standard Schema dentro de
type(), por lo que un esquema existente de Zod 4 puede anidarse en una definición de ArkType sin necesidad de reescribirlo primero.
¿Cuál es la diferencia esencial entre ArkType y Zod?
Zod construye un esquema a partir de llamadas a métodos encadenados, mientras que ArkType lo escribe como cadenas similares a TypeScript, de modo que se lee igual que el tipo que genera. Un campo declarado como "(number | string)[]" es la anotación de TypeScript que habrías escrito de todos modos, solo que entre comillas. La guía “Your First Type” de ArkType señala que el editor comprueba estas definiciones en forma de cadena mientras escribes, con autocompletado, utilizando el propio sistema de tipos de TypeScript.
El mismo esquema User en Zod y en ArkType
A continuación se muestra el mismo esquema de tres campos en ambas bibliotecas: una cadena obligatoria, una unión de dos valores y un array opcional de números o cadenas.
Zod 4:
import * as z from "zod"
const User = z.object({
name: z.string(),
platform: z.enum(["android", "ios"]),
versions: z.array(z.union([z.number(), z.string()])).optional(),
})
ArkType 2.2:
import { type } from "arktype"
const User = type({
name: "string",
platform: "'android' | 'ios'",
"versions?": "(number | string)[]",
})
La versión de ArkType no tiene llamadas anidadas a constructores (builders). La opcionalidad también cambia de lugar: ArkType marca la clave con ?, como hace TypeScript, mientras que Zod llama a .optional() sobre el valor. ArkType también admite una configuración global exactOptionalPropertyTypes (añadida en la versión 2.1.12) que se corresponde con la opción del compilador de TypeScript del mismo nombre.
| Criterio | Zod 4 | ArkType 2.2 |
|---|---|---|
| Sintaxis del esquema | Métodos constructores encadenados | Cadenas similares a TypeScript y literales de objeto |
| Campo opcional | .optional() sobre el valor | "key?" sobre la clave |
| Tipo estático | z.infer<typeof User> | typeof User.infer |
| Validar datos desconocidos | User.safeParse(data) | User(data) |
| Comprobación de fallo | !result.success | out instanceof type.errors |
| Texto de error legible | Construido a partir de result.error.issues | out.summary |
| Compilación | Opcional mediante z.compile() (Zod 4.5+) | Integrada en el procesamiento de las definiciones |
Inferencia de tipos: typeof User.infer frente a z.infer
Ambas bibliotecas derivan el tipo estático a partir del esquema de tiempo de ejecución, así que nunca tienes que escribir la interfaz a mano. Lo único que cambia es la sintaxis de extracción.
// Zod
type User = z.infer<typeof User>
// ArkType
type User = typeof User.infer
En Zod, z.infer es una utilidad genérica que se aplica al tipo del esquema. En ArkType, infer es una propiedad del propio tipo, a la que se accede mediante typeof. Con el esquema anterior, ambas producen la misma forma: { name: string; platform: "android" | "ios"; versions?: (number | string)[] }.
¿Es ArkType más rápido que Zod?
ArkType genera un validador optimizado de antemano, en el momento en que se crea cada Type. La documentación de configuración de ArkType describe este paso de precompilación y una opción jitless que lo desactiva. La página principal de ArkType afirma que ArkType es 20 veces más rápido que Zod 4 en tiempo de ejecución. Se trata de un benchmark del propio proveedor, no de una medición independiente.
Desde entonces, Zod 4.5 ha reducido parte de esa diferencia con z.compile(). Como explica la documentación de compilación de Zod, z.compile() recorre el esquema una sola vez y genera una función de comprobación lineal, que Zod ejecuta con new Function(). Si la entrada no supera esa comprobación rápida, Zod la delega al parser normal, de modo que los errores detallados se mantienen iguales. El README de Zod indica una aceleración mediana de 2,4x en un benchmark de 55 esquemas. Por lo tanto, una comparación con Zod 4 sin compilar no indica cómo se comporta ArkType frente a un esquema de Zod compilado.
En la validación de formularios, la diferencia de velocidad entre ArkType y Zod rara vez importa. Unas pocas validaciones por cada interacción del usuario no van a ser tu cuello de botella. La velocidad cobra relevancia cuando un mismo esquema se ejecuta miles de veces por segundo, por ejemplo, en manejadores de peticiones o en importaciones por lotes.
Validación de extremo a extremo de la respuesta de una API
La tarea real más habitual es comprobar la respuesta de un fetch antes de que el resto de la aplicación confíe en ella. Esta es la misma función en ambas bibliotecas.
ArkType:
async function getUser(id: string) {
const res = await fetch(`/api/users/${id}`)
const out = User(await res.json())
if (out instanceof type.errors) {
console.error(out.summary)
return null
}
return out // typed as User
}
Zod:
async function getUser(id: string) {
const res = await fetch(`/api/users/${id}`)
const result = User.safeParse(await res.json())
if (!result.success) {
console.error(result.error.issues)
return null
}
return result.data
}
Zod y ArkType devuelven estructuras distintas al validar. El safeParse de Zod devuelve un objeto de resultado discriminado con success, data y error. Un tipo de ArkType devuelve directamente el valor validado o una instancia de ArkErrors. Tras la comprobación con instanceof, TypeScript acota (narrowing) out al tipo User. out.summary es un único mensaje legible que enumera cada ruta con fallos, lo que se esperaba en ella y lo que se recibió.
Además, ArkErrors puede pasarse directamente a JSON.stringify(), un cambio que llegó en ArkType 2.1.10 y que figura en las notas de la versión 2.2. Esto significa que un fallo de validación puede incluirse directamente en una respuesta de error de la API o en una entrada de log sin tener que escribir antes un formateador.
La contrapartida: ecosistema y familiaridad frente a una gramática nueva
La principal ventaja de Zod sobre ArkType es todo lo que lo rodea. Cuenta con un amplio ecosistema de integraciones, y la mayoría de los desarrolladores de TypeScript ya saben leer un esquema de Zod. Parte de esa brecha se está reduciendo gracias a Standard Schema, una interfaz común que implementan Zod, ArkType y Valibot. Antes de cambiar, comprueba si tu biblioteca de formularios, tu router y tu capa de RPC aceptan Standard Schema.
Los costes de ArkType son reales:
- Una gramática nueva. Las uniones, los arrays y las claves opcionales resultan familiares, pero las restricciones y las expresiones más avanzadas forman un lenguaje de cadenas que tu equipo tendrá que aprender.
- Los errores aparecen como errores de tipo sobre cadenas. TypeScript detecta en el editor una errata como
"strng", pero como un error en una definición de cadena y no como un método inexistente, por lo que tu equipo tendrá que acostumbrarse a interpretar un nuevo tipo de mensaje de error.
ArkType 2.2 hace posible una adopción parcial. La documentación de integraciones de ArkType muestra que type() acepta cualquier validador compatible con Standard Schema, por sí solo o dentro de una definición de objeto, y lo infiere y comprueba como si fuera una definición nativa de ArkType. Esto significa que los esquemas existentes de Zod 4 pueden quedarse como están:
import * as z from "zod"
import { type } from "arktype"
const ZodDevice = z.object({ platform: z.enum(["android", "ios"]) })
const User = type({
name: "string",
device: ZodDevice, // Standard Schema validator nested in ArkType (2.1.28+)
})
Si tu principal limitación es el tamaño del bundle y no la velocidad, la biblioteca que debes considerar es Valibot.
¿Cuándo conviene pasar de Zod a ArkType?
La mayoría de los equipos con una base de código estable en Zod e integraciones que funcionan no deberían cambiar a ArkType todavía. El coste de la migración supera una ganancia de velocidad que el compilador de Zod 4.5 ya cubre en parte. Prueba ArkType cuando:
- Estés empezando un proyecto nuevo y tus herramientas acepten Standard Schema.
- Tengas una ruta crítica demostrada, como un endpoint de alto rendimiento o un proceso por lotes, en la que el profiling muestre que la validación tiene un coste apreciable.
- Quieras probarlo en un solo endpoint, anidando los esquemas de Zod existentes dentro de definiciones de ArkType en lugar de reescribirlos.
En caso contrario, sigue validando datos con Zod y prueba z.compile() en los esquemas que se ejecutan con más frecuencia.
Conclusión
La principal ventaja de ArkType es la legibilidad: el esquema se parece al tipo que genera, y además te proporciona un validador compilado y rápido. La ventaja de Zod es que ya está integrado en tu stack. Para comprobar la diferencia con poco esfuerzo, elige un endpoint con mucha carga de validación, defínelo con ArkType 2.2 anidando tus esquemas de Zod existentes y compáralo mediante profiling con una versión z.compile() del mismo esquema de Zod antes de cambiar cualquier otra cosa.
Preguntas frecuentes
¿Funciona ArkType en Cloudflare Workers o bajo una Content Security Policy estricta?
Sí. ArkType precompila la lógica de validación con new Function cuando se instancia un Type, y desactiva este comportamiento automáticamente en entornos que no admiten new Function, como Cloudflare Workers. Con una CSP sin 'unsafe-eval', establece la opción jitless en true. Configúrala desde 'arktype/config' antes de importar nada de 'arktype' para que las palabras clave integradas la tengan en cuenta. La validación sigue funcionando, solo que sin los validadores precompilados.
¿Puede ArkType generar JSON Schema como hace Zod 4?
Sí. Todo Type de ArkType tiene un método toJsonSchema(), y ArkType 2.2 añadió el paquete @ark/json-schema para la dirección inversa, que convierte JSON Schema en Types de ArkType. Las características sin equivalente en JSON Schema, como los morphs, las claves de tipo symbol o Date, hacen que toJsonSchema() lance una excepción por defecto, y una opción fallback permite gestionar cada caso. Zod 4 cubre la misma necesidad con z.toJSONSchema().
¿Cuál es el equivalente en ArkType del transform() de Zod?
ArkType denomina morphs a las transformaciones y las asocia con .pipe(), por ejemplo, type('string').pipe(s => s.trim()). Las palabras clave de parseo integradas, como 'string.json.parse' y 'string.numeric.parse', gestionan las conversiones habituales sin necesidad de un callback. Si un morph lanza una excepción, ArkType asume que querías que el programa fallara. Usa .pipe.try() para convertir la excepción lanzada en un resultado ArkErrors. El tipo inferido refleja la salida del morph.
¿Puede ArkType lanzar una excepción ante datos no válidos, como el parse() de Zod, en lugar de devolver errores?
Sí. Llamar a out.throw() sobre un resultado ArkErrors lo lanza como excepción, y la opción global onFail hace que todos los Types lancen una excepción ante datos no válidos: configure({ onFail: errors => errors.throw() }) desde 'arktype/config'. Declara el mismo onFail en la interfaz global ArkEnv para que TypeScript sepa que las invocaciones ya no devuelven ArkErrors. El estilo predeterminado basado en el valor de retorno equivale al safeParse() de Zod, y onFail equivale a parse().
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k