Cómo evitar el uso de `any` en TypeScript y mejorar tu código
¿Sigues parcheando con any porque “es más rápido”? Felicidades: acabas de convertir a tu compilador en un cómplice silencioso de los bugs nocturnos.
Tiempo estimado de lectura: 5 min
- TypeScript no te salva por arte de magia: es una herramienta que hay que configurar y aplicar, no decoración del IDE.
- No uses any como parche: usa unknown o validación runtime para datos externos.
- Activa strict y prioriza validación en la frontera: tsconfig, linters, CI y validadores como Zod.
Introducción
Poca gente lo dice tan claro: TypeScript no te salva automáticamente. Si lo tratas como decoración del IDE, te dará una falsa sensación de seguridad. Y cuando las cosas se rompan en producción, nadie recordará quién puso ese as any a las tres de la mañana.
Voy a ser directo. Esto es lo que rompe proyectos y cómo lo arreglas para que deje de romperlos.
Resumen rápido (lectores con prisa)
TypeScript proporciona tipos estáticos para detectar errores tempranos en desarrollo. No valida tipos en runtime; para datos externos hay que usar validación en la frontera (por ejemplo Zod) y mantener "strict": true en tsconfig. Evita any y el operador de aserción no-nula !; prefiere unknown, encadenamiento opcional y guard clauses. Integra linters y CI que ejecuten tsc --noEmit y pruebas de contratos para evitar deuda técnica silenciosa.
Qué falla y cómo lo arreglas
1) Deja de usar any como parche rápido
any = apagar las comprobaciones. unknown = obligarte a pensar.
Usa unknown cuando no conoces la forma de un dato. Forcear any es como cerrar los ojos y conducir a 140 km/h: puedes llegar, o no.
Ejemplo idiota, pero real:
// NO
function process(payload: any) {
console.log(payload.name.toUpperCase());
}
// SÍ
function processSafe(payload: unknown) {
if (typeof payload === 'object' && payload !== null && 'name' in payload) {
console.log((payload as { name: string }).name.toUpperCase());
}
}
No te apetece escribir esa comprobación ahora. Perfecto: pon una validación con Zod y delega la detección a la frontera.
2) Activa strict. No negociable.
Si tu tsconfig dice "strict": false estás firmando cheques a la deuda técnica.
Síntomas: parámetros sin tipado, nulos que aparecen sin avisar, inicialización incompleta de clases. Solución simple: "strict": true y arreglar las fallas una por una. ¿Migración? Usa @ts-expect-error puntualmente. No rebajes la seguridad global.
3) El operador ! es una mentira elegante
usuario.address!.street parece limpio. Es una bomba.
Mejor: encadenamiento opcional o guard clauses.
usuario.address?.street— seguro, devuelve undefined.if (!usuario.address) return— claro, explícito.
Si ven ! en un PR, que explique por qué. Si no puede explicar, rechaza el PR.
4) as T NO valida datos externos
const data = await res.json() as User es confiarle la vida a un string.
En la frontera (red, persistencia, input externo), usa validación runtime. Zod, io-ts, AJV: cualquiera que genere el guard que puedas correr al recibir datos. Y sí: extrae el esquema a un único lugar para que el tipo y el validador sean la misma verdad.
Ejemplo con Zod:
import { z } from 'zod';
const UserSchema = z.object({ id: z.string(), name: z.string() });
type User = z.infer;
const raw = await res.json();
const user = UserSchema.parse(raw); // lanza si no concuerda
5) La “gimnasia de tipos” rompe equipos
Type Gymnastics — esos tipos de 40 líneas que solo el autor entiende — son deuda técnica disfrazada. Úsalos donde aporten valor: librerías, infra, utilities core. No en cada componente. Si un tipo necesita 30 minutos para entenderlo, lo estás usando mal.
Buenas prácticas: alias cortos, nombres claros, ejemplos en comentarios y tests de tipos (tsd) para que no se rompa en silencio.
Checklist operativo (copia y pega y aplica ya)
- tsconfig.json:
"strict": true - ESLint: activar reglas
@typescript-eslint/no-explicit-any: error@typescript-eslint/strict-boolean-expressions: warn/error
- Pre-commit: husky + lint-staged para bloquear
anyy! - CI: job que ejecuta
tsc --noEmity tests de contratos (Zod parse) - Boundary validation: todas las respuestas HTTP parseadas con Zod/io-ts
- Tests de tipos con
tsden PRs críticos
Snippet de CI (esqueleto GitHub Actions)
name: Validate Types
on: [pull_request]
jobs:
types:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: pnpm install
- run: pnpm build # incluye tsc --noEmit
- run: pnpm test:contracts # tests que hacen parse de esquemas Zod
Cómo aplicarlo en equipos (cultura > herramientas)
- Regla simple: todo dato que venga de fuera pasa por un esquemas.
- PRs deben documentar la asunción: “Por qué este
!está justificado”. - Prohibe
anyen lint. No excepciones. - Revisión de tipos en pair programming para cambios complejos.
- Añade tests e2e que verifiquen la integración real del esquema (no solo mocks).
Errores típicos y la respuesta corta
- “No activamos strict porque produce demasiados errores” → Activarlo e ir resolviendo; cada corrección es una deuda pagada.
- “Validar en runtime es caro” → El coste es mínimo comparado con el tiempo perdido debugueando un null en producción.
- “Los tipos complejos son elegantes” → Sí. Elegantes y peligrosos si nadie los entiende.
Mini-guía de herramientas que te salvan la vida
- Zod: validación runtime + inferencia de tipos. Ganador simple.
- @typescript-eslint: reglas para prohibir
any,!, etc. - tsd: tests de tipos que evitan roturas silenciosas.
- husky + lint-staged: bloqueo en pre-commit de malas prácticas.
- Sentry / logs: cuando la validación falla en producción, registra payload + endpoint.
Métrica que deberías vigilar semanalmente
- PRs con
anyencontrados: objetivo = 0. - Rechazos por
!sin justificación: objetivo = 0. - Número de validaciones runtime faltantes en endpoints críticos: objetivo = 0.
Si tienes más de 1 en cualquiera, tienes trabajo de deuda técnica.
Cierre sin azúcar: esto es disciplina, no postureo
TypeScript te da herramientas para poner reglas fuertes y claras en el código. Sin disciplina, esas reglas se convierten en adorno. Cambiar la cultura es más duro que tocar tsconfig, pero mucho más rentable.
¿Quieres algo práctico para arrancar mañana? Te puedo enviar:
- Un repo template con tsconfig, ESLint, husky, Zod y pruebas de contratos listas.
- Un workflow de GitHub Actions que falla el CI si hay
anyo!no justificado. - Un checklist PDF para code reviews centrado en tipado seguro.
Respóndeme “Envíame el template” o “Quiero el workflow” y te lo paso listo. No es sexy. Es lo que evita que pases la noche arreglando prod por culpa de un as any. Esto no acaba aquí.
FAQ
- ¿Por qué no debo usar any?
- ¿Cuándo usar unknown en lugar de any?
- ¿Qué hace exactamente “strict”: true?
- ¿Cómo valido respuestas HTTP correctamente?
- ¿Qué hacer si activar strict rompe demasiados archivos a la vez?
- ¿Cómo evitar que los tipos complejos sean incomprensibles?
¿Por qué no debo usar any?
Porque desactiva las comprobaciones de tipos y oculta errores que el compilador podría detectar. Usar any convierte el compilador en cómplice de bugs que aparecen en producción.
¿Cuándo usar unknown en lugar de any?
Usa unknown cuando recibes datos sin estructura conocida. Obliga a validar o refinar el tipo antes de operar sobre el valor, evitando asunciones peligrosas.
¿Qué hace exactamente “strict”: true?
Activa un conjunto de opciones de compilador que refuerzan la seguridad de tipos: strictNullChecks, noImplicitAny, strictBindCallApply, entre otras. Detecta parámetros sin tipado, nulos no manejados y problemas de inicialización.
¿Cómo valido respuestas HTTP correctamente?
Usa validación runtime en la frontera con esquemas compartidos (por ejemplo Zod). Parseas el payload recibido con el esquema y manejas el error si no concuerda antes de propagar datos al resto de la aplicación.
¿Qué hacer si activar strict rompe demasiados archivos a la vez?
Actívalo y corrige las fallas progresivamente. Para casos puntuales usa @ts-expect-error temporalmente, pero no como solución permanente.
¿Cómo evitar que los tipos complejos sean incomprensibles?
Prefiere alias cortos y nombres claros, añade ejemplos y tests de tipos (tsd) y reserva tipos largos para librerías o infraestrutura donde el equipo esté alineado.
