TypeScript
Validação de Dados
Zod
Qualidade de Software
Arquitetura

Validación de datos con Zod: por qué los tipos TypeScript no son suficientes

Los tipos de TypeScript son garantías de compilación, no garantías de ejecución. La validación de esquema primero con Zod crea una única fuente de verdad entre el tipo y los datos.

Existe un costoso malentendido en los equipos que han adoptado TypeScript con entusiasmo. La creencia de que al tener tipos, el sistema está protegido contra datos malformados. No lo es. Y esta confusión entre lo que hace el compilador y lo que no hace es el origen de toda una categoría de errores que sólo aparecen en producción.

Quiero disipar este malentendido directamente, porque influye en las decisiones arquitectónicas. Cualquiera que comprenda dónde terminan los tipos empieza a tratar los límites del sistema con el cuidado que requieren.

Lo que realmente garantiza TypeScript

TypeScript es un sistema de tipos que vive completamente en tiempo de compilación. Usted escribe las anotaciones, el compilador las verifica y luego se eliminan. El JavaScript que se ejecuta en producción no sabe que ese objeto era un Usuario. Para el motor de ejecución, es cualquier objeto.

Esto es por diseño, no por un defecto. TypeScript fue creado para no imponer ningún costo en tiempo de ejecución. La consecuencia es que cada tipo de garantía es una promesa sobre el código que usted escribió, no los datos que procesará.

Mientras los datos nazcan dentro de su código, la promesa se cumple. Una función que toma un número y devuelve un número está protegida por el compilador de principio a fin. El problema empieza cuando los datos vienen del exterior.

La frontera donde se evapora la garantía

Piense en todo lo que ingresa a su sistema sin ser creado por él. La respuesta de una API externa. El cuerpo de una solicitud HTTP. Un formulario completado por un humano distraído. Una línea leída de base de datos después de una migración fallida. Una variable de entorno. Un mensaje en una cola.

En todos estos puntos, normalmente hace algo como declarar que los datos que recibe son de un determinado tipo. Una afirmación. Y aquí está el error: esta afirmación no verifica nada. Simplemente le dice al compilador que confíe en usted y siga adelante. Si la API cambió un campo de número a texto, TypeScript continúa creyendo que es un número, porque usted le dijo que lo creyera.

El resultado es un sistema que parece tipificado pero que tiene agujeros justo en los bordes, donde entra el mundo real. El error no ocurre en la frontera, donde sería fácil de diagnosticar. Ocurre en tres capas de profundidad, cuando algo intenta utilizar ese campo como si la promesa fuera cierta. El rastro se enfría, el seguimiento de la pila apunta al lugar equivocado y alguien pierde la tarde.

Validación en tiempo de ejecución es lo que faltaba

La solución conceptual es simple de plantear y fácil de posponer: en los puntos de entrada, es necesario verificar, en tiempo de ejecución, que los datos tengan la forma esperada. No te fíes, compruébalo.

La validación en tiempo de ejecución significa código que analiza los datos de manera efectiva y responde si son válidos o no. Si la API prometió un número y envió un mensaje de texto, la validación grita allí, en el borde, con un mensaje claro, antes de que el valor contamine el resto del flujo. Se cambia un error silencioso y profundo por un fallo ruidoso y localizado.

Históricamente, esto fue laborioso y fácil de olvidar. Escribimos la interfaz TypeScript por un lado y, por el otro, una función de validación separada que verificaba los mismos campos a mano. Dos descripciones de la misma cosa, realizadas por diferentes personas, en diferentes momentos. Ellos diferían. Siempre difieren. El tipo decía una cosa, el validador comprobaba otra y los datos reales seguían una tercera regla.

Zod y el enfoque de esquema primero

Aquí es donde Zod cambia la forma en que pensamos sobre el problema. La idea central es invertir el orden: en lugar de escribir el tipo y luego un validador que intenta seguirle el ritmo, se escribe un esquema único y el tipo se deriva de él automáticamente.

Describes la forma de los datos una vez, con sus reglas, campos obligatorios, formatos y límites. De este esquema Zod extrae dos cosas al mismo tiempo. Uno es el validador que se ejecuta en producción y realmente examina los datos. El otro es el tipo estático TypeScript], generado a partir de la misma definición, sin que tengas que escribirlo a mano.

Éste es el beneficio que importa: una única fuente de verdad. El tipo y la validación ya no pueden divergir, porque provienen del mismo lugar. Si cambia el esquema, el tipo cambia junto con él y cualquier código que dependa del formato anterior no se podrá compilar. La divergencia ya no es posible mediante la construcción, en lugar de evitarse mediante la disciplina.

Los datos externos entran, pasan a través del esquema y salen por el otro lado como un valor que el compilador ahora puede tratar con confianza legítima, porque la confianza se ganó en tiempo de ejecución y no solo se declaró. La afirmación ciega se convierte en una verificación real y, a partir de entonces, el resto del sistema vuelve a estar protegido por tipos.

¿Qué cambia esto en la mente de quienes deciden?

Adoptar la validación primero del esquema no es una elección de herramienta, es una elección de dónde colocar el límite de confianza. La regla que propongo al equipo es sencilla: nada de lo que viene de fuera entra sin pasar por un esquema. API, formulario, banco, cola, configuración. Todo se valida en el borde.

Dentro de este límite, confías completamente en los tipos, porque han vuelto a corresponder a la realidad. Fuera de él, no confías en nada que no haya sido verificado. Esta clara separación entre el territorio validado y el salvaje es lo que hace que el sistema sea predecible. Cualquiera que trabaje con TypeScript en proyectos avanzados sabe que el tipo es una herramienta de razonamiento y solo razona bien sobre datos que realmente tienen la forma prometida.

Vale la pena anotar el costo, que es honesto y pequeño. Hay un esfuerzo inicial para modelar los esquemas y una sobrecarga de ejecución para validar los datos en los bordes. A cambio, eliminas una clase de errores que salen caros precisamente porque se manifiestan tarde y lejos de la causa. Es uno de los mejores retornos de la inversión que conozco en calidad de software y habla directamente de prácticas más amplias de seguridad de aplicaciones web, ya que la validación de datos también es la primera línea de defensa.

Si lidera un equipo que ha adoptado TypeScript pero aún trata datos externos con afirmaciones ciegas, vale la pena revisar los límites del sistema esta semana. El costo de agregar validación donde entran los datos es bajo, y el costo de no tenerla es exactamente el tipo de error que nadie quiere depurar los viernes.

Lea también