Fundamentos de Zod
Van tres errores hasta ahora, todos con la misma forma. Llegó un valor, el código lo usó y nadie lo verificó antes.
Corregir cada uno donde aparece funciona, pero es interminable. Cada nuevo destino es un lugar más que hay que recordar.
La otra opción es verificar el valor una sola vez, apenas llega, contra una descripción de lo que esperabas. Esa descripción es un schema, y Zod es la librería que esta sección usa para escribir uno.
Qué es un schema
Imagina el escáner de un aeropuerto. Tu maleta entra por un lado. La máquina está configurada con reglas: nada de objetos filosos, nada de líquidos que superen cierto tamaño. Una maleta que cumple las reglas sale por el otro lado sin cambios. Una que no las cumple jamás pasa.
Un schema son los ajustes de la máquina. Parsear es hacer pasar la maleta por ella.
Instálalo e impórtalo:
npm install zodimport * as z from 'zod'El schema más simple es una sola regla sobre un solo valor:
const teacherSchema = z.string()
const teacher = 'Mateo'
console.log(teacherSchema.parse(teacher))
// 'Mateo'parse le entrega los datos a la máquina. Los datos cumplen la regla, así que vuelven intactos.
Dale algo que no encaje y la máquina se detiene:
teacherSchema.parse(12345)
// ZodError: Invalid input: expected string, received numberEse es todo el modelo. Describes lo que es aceptable, haces pasar los valores, y obtienes el valor de vuelta o un error.
El schema es la descripción de lo que vas a aceptar. Parsear es el acto de verificar algo contra esa descripción. Escribes el schema una vez y lo usas para parsear tantas veces como quieras.
Describir un objeto
Un profesor rara vez es un solo string. Mejor dale una forma al schema:
const teacherSchema = z.object({
name: z.string(),
age: z.number(),
})
const teacher = {
name: 'Mateo',
age: 21,
}
console.log(teacherSchema.parse(teacher))
// { name: 'Mateo', age: 21 }Cada entrada dentro de z.object() es una clave, y su valor es el schema de esa clave. Ahora la máquina espera un objeto con un name que sea cualquier string y una age que sea cualquier número.
Rompe una de ellas y el error dice exactamente cuál:
teacherSchema.parse({ name: 'Mateo', age: '21' })
// ZodError: Invalid input: expected number, received stringZod trae los primitivos que uno esperaría, z.string(), z.number(), z.boolean(), y los objetos se anidan dentro de otros objetos tan profundo como lo necesiten tus datos.
Así, la misma idea cubre tanto un formulario de dos campos como la respuesta de una API con una estructura profunda. Siempre estás describiendo un nivel a la vez.
Por qué la verificación tiene que pasar en tiempo de ejecución
TypeScript también describe formas:
type Teacher = {
name: string
age: number
}Esto se parece al schema, pero hace un trabajo completamente distinto. TypeScript verifica los tipos mientras escribes el código. Cuando compila a JavaScript, cada anotación desaparece, así que nada de eso sobrevive en el programa en ejecución.
Eso está bien para los valores que produce tu propio código, y no sirve de nada para los que no. El cuerpo de una solicitud, la respuesta de una API, el envío de un formulario: todo eso llega mientras el programa ya está corriendo, mucho después de que los tipos dejaron de existir. TypeScript asume que tus datos son correctos. Zod los verifica.
Un schema es la descripción de un tipo que todavía existe cuando los datos llegan.
TypeScript es una conversación contigo mientras escribes. El schema es una conversación con los datos mientras el programa se ejecuta.
Ponlo en práctica
Escríbelo desde cero, sin copiar el ejemplo del profesor.
- Importa Zod.
- Crea un
characterSchemacon dos claves:name, un string, yepisode, un número. - Define un objeto
charactercon el nombre'Luke Skywalker'y episodio4. - Valida el personaje contra el schema y muestra el resultado en consola.
- Cambia
episodeal string'4'y predice qué va a pasar antes de ejecutarlo.
Compara tus respuestas
import * as z from 'zod'
const characterSchema = z.object({
name: z.string(),
episode: z.number(),
})
const character = {
name: 'Luke Skywalker',
episode: 4,
}
console.log(characterSchema.parse(character))
// { name: 'Luke Skywalker', episode: 4 }Dos claves significan z.object() en lugar de un primitivo simple, y cada clave tiene su propio schema.
Con episode como '4', parse lanza ZodError: Invalid input: expected number, received string. El string '4' no es un número, y Zod no lo va a convertir por ti en silencio. Convertir a propósito es una instrucción aparte, que se cubre en el próximo capítulo.
El episodio 4 es correcto, dicho sea de paso. Luke Skywalker aparece por primera vez en la película original de Star Wars de 1977, que después fue numerada como Episodio IV.
Hacia dónde va esto
Por ahora un schema hace una sola cosa: acepta un valor o lanza una excepción. Eso alcanza para describir datos, pero todavía no alcanza para construir con ellos.
Inferir tipos y forzar la conversión de datos de entrada agrega las dos piezas que lo hacen práctico. Un mismo schema también le puede dar el tipo a TypeScript, así que la forma se escribe una sola vez. Y un parseo fallido puede darte un resultado para inspeccionar en lugar de una excepción para atrapar.

