Despliegue gradual del modo estricto de TypeScript para aplicaciones generadas por IA
Despliegue por etapas del modo estricto de TypeScript para aplicaciones generadas por IA que reduce errores en tiempo de ejecución, mantiene los PR pequeños y evita bloqueos en la migración.

Qué cambia el modo estricto (y por qué las apps generadas por IA se rompen)
El modo estricto de TypeScript no es una sola regla. Es un conjunto de comprobaciones que impide que el compilador acepte suposiciones. Activarlo funciona mejor cuando lo tratas como añadir mejores detectores de humo, no como reconstruir toda la casa.
Cuando pones "strict": true en tsconfig, TypeScript activa varias opciones que a menudo están desactivadas en prototipos. Las más visibles son:
strictNullChecks: te obliga a manejarnullyundefineda propósitonoImplicitAny: evita tipos “cualquiera” silenciososstrictFunctionTypes: detecta parámetros de función peligrososnoImplicitThis: evita el problema de “this es cualquier cosa”alwaysStrict: aplica las reglas de strict mode de JavaScript
Las apps generadas por IA suelen fallar con estas comprobaciones porque “funcionan” apoyándose en un tipado débil y suposiciones ocultas: parsear respuestas de API como any, asumir que un campo siempre existe, tratar la entrada de usuario como el tipo correcto o pasar objetos a medio formar entre capas. Todo parece bien hasta que un usuario real golpea un caso límite. Entonces aparecen errores por undefined, flujos de autenticación rotos o datos con una forma diferente a la que espera la UI.
El modo estricto reduce los errores en tiempo de ejecución haciendo que estos problemas sean ruidosos durante el desarrollo. Suele detectar:
- acceder a propiedades de valores que pueden ser
undefined - mezclar formas de datos (por ejemplo,
{ id: string }vs{ id: number }) - llamar funciones con argumentos faltantes o equivocados
- uso inseguro de
anyque oculta errores de tipado reales
El primer encendido suele sentirse ruidoso. Eso es normal. Piénsalo como un refactor controlado: estás añadiendo claridad y barreras en áreas pequeñas, no reescribiendo la app.
Inventario rápido: mapea las partes sucias antes de cambiar la configuración
Antes de tocar las opciones de strict, dedica una hora a mapear dónde es más probable que se oculten bugs en tiempo de ejecución. En proyectos generados por IA, un cambio en un archivo “central” puede hacer que los errores exploten por todo el repositorio.
Empieza por localizar por dónde entra la información en la app y dónde se reutiliza. Los tipos débiles se propagan más rápido a través de:
- llamadas a API y helpers del cliente (wrappers de fetch, instancias de axios, parseo de respuestas)
- autenticación y manejo de sesión (almacenamiento de tokens, objetos de usuario, middleware)
- capa de base de datos y consultas (modelos ORM, SQL crudo, migraciones)
- utilidades compartidas y constantes (helpers de fecha, acceso a env, feature flags)
- código “pegamento” entre páginas/componentes y servicios (mapear JSON a estado UI)
Después, busca patrones que el modo estricto vaya a exponer. No los arregles todavía, solo localízalos: muchos any, parámetros con implicit any, JSON sin tipar, campos opcionales usados como si siempre existieran y IDs “stringly typed” que se mezclan (userId vs orgId).
Decide el alcance pronto para que el trabajo siga siendo manejable. Puedes escalonar qué partes chequeas: primero el código de la app, luego tests, scripts y herramientas de build. Un punto de partida común es “solo código de la app”, así no te bloquean scripts de desarrollo puntuales.
Finalmente, lleva un documento ligero de notas de migración. Registra tipos de error recurrentes (como “Object is possibly undefined”) y tu solución preferida (cláusula guard, valor por defecto o mejor tipo). Cuando arregles el trigésimo error similar, agradecerás haberlo documentado.
Elige una estrategia por etapas que mantenga el trabajo manejable
Un despliegue de strict tiene éxito cuando eliges unidades de trabajo que puedas terminar. Las apps generadas por IA suelen tener calidad desigual: una carpeta está bien, la siguiente está llena de implicit any, checks nulos ausentes y tipos copiados. Escalonar te mantiene avanzando sin convertir todo el código en una pared roja.
Escoge una unidad de migración que coincida con la organización del proyecto: por carpeta (proyectos por capas), por feature (rutas + UI + servicios), por paquete (monorepos) o por punto de entrada (un worker, webhook o job runner que cause la mayoría de los crashes).
Sigue publicando mientras migras. Evita una rama de “modo estricto” de larga vida que derive durante semanas. Trabaja en PRs pequeños y fusiona a menudo. Si la strictness no está activada en todas partes todavía, trata el “área estricta” como protegida: los archivos nuevos dentro deben estar limpios y los cambios fuera no deben debilitar tipos.
También asigna a una persona para decidir los patrones de migración. No se trata de jerarquía, sino de consistencia. Alguien debe responder preguntas como: cuándo usar unknown vs any, cómo nombrar tipos compartidos y cuándo una comprobación rápida basta versus cuándo merece la pena refactorizar.
Para cada chunk, define claramente “hecho”: las comprobaciones estrictas pasan para ese chunk, los tests pasan (o añades un test pequeño para la ruta riesgosa), las escapatorias temporales están registradas y el código sigue siendo legible.
Plan de despliegue paso a paso (pasos pequeños, menos sorpresas)
Empieza por asegurarte de poder decir si rompiste el comportamiento. Las apps generadas por IA suelen “funcionar” porque nada comprueba casos límite, no porque el código sea seguro.
Congela el comportamiento actual con un build que se ejecute igual siempre. Añade algunas comprobaciones de humo que cubran las rutas principales: iniciar sesión, cargar una página clave, enviar un formulario y llamar a un endpoint de API. Mantenlas simples, pero repetibles.
Luego despliega la strictness en rebanadas contenidas. El objetivo es menos bugs en tiempo de ejecución sin convertir todo en un refactor gigante.
- Establece la línea base: haz que
tscse ejecute en CI (o localmente) y asegúrate de que el proyecto compile limpio en su estado actual. Añade un script de humo pequeño o una checklist manual que puedas ejecutar en 2 minutos. - Limita la strictness a un área segura primero: aplica opciones más estrictas a una sola carpeta (una nueva feature, un paquete o un módulo) para aprender los patrones de error sin detener todo el trabajo.
- Arregla los límites de tiempo de ejecución de mayor riesgo: céntrate en los puntos donde entra datos desconocidos en el sistema, como respuestas de API, cuerpos de request, vars de entorno, localStorage y lecturas de la base de datos. Añade parseo/validación y haz que los tipos reflejen la realidad.
- Expande gradualmente con PRs pequeños: mantén los cambios focalizados (un tema por PR), por ejemplo “arreglar
anyen el cliente de API” o “añadir comprobaciones nulas en el flujo de auth”. Los PRs pequeños son más fáciles de revisar y menos propensos a ocultar cambios de comportamiento. - Activa strict para todo el proyecto cuando el recuento de errores sea bajo: cuando estés reducido a un número manejable, habilita strict globalmente y limpia los puntos calientes restantes.
Ejemplo: si tu prototipo a veces falla con “Cannot read property 'id' of undefined”, la strictness normalmente te señalará la cadena donde user puede ser null. Arreglarlo una vez en el límite de auth a menudo elimina toda una clase de bugs.
Qué banderas de strict activar primero (y por qué el orden importa)
Si activas todo de una vez en código desordenado, a menudo obtendrás una pared de errores sin camino claro. Una mejor táctica es encender comprobaciones en un orden que mantenga las correcciones locales y las revisiones comprensibles.
Una secuencia práctica para bases de código desordenadas es:
noImplicitAny: te obliga a nombrar tipos poco claros en lugar de usaranysilenciosamentestrictNullChecks: evita los clásicos fallos de “cannot read property of undefined”strictBindCallApply: detecta llamadas a funciones erróneas (común en utilidades copiadas)noImplicitThis: previene usos confusos dethisen patrones antiguosuseUnknownInCatchVariables: hace que el manejo de errores sea más seguro que asumirany
Después de cada bandera, arregla los nuevos errores y luego pasa a la siguiente. Mantienes los cambios pequeños y aprendes qué partes de la app son realmente frágiles.
Si debes establecer "strict": true depende de tu situación. Si tienes tests sólidos y una base de código pequeña, puede estar bien. Para apps generadas por IA con límites poco claros, habilitar banderas una por una suele ser más seguro.
También puedes controlar el radio de impacto con include y exclude. Empezar con src/ y saltarte áreas problemáticas temporalmente suele marcar la diferencia entre “progreso” y “atasco”.
Trata algunas carpetas de forma diferente:
- salida generada: exclúyela y type-checkea la fuente en su lugar
- código de terceros: no lo edites; envuélvelo con adaptadores tipados
- carpetas legacy: aíslalas y añade tipos solo donde tocan código nuevo
- mezcla JS/TS: convierte puntos de entrada primero, no cada archivo a la vez
Patrones de corrección que eliminan errores sin sobreingeniería
Los mayores aciertos vienen de arreglar unos pocos patrones de error de forma consistente.
Empieza con el implicit any. Añade tipos en los límites primero: entradas de funciones, tipos de retorno y cualquier cosa que cruce una línea de módulo (cliente de API, helpers de base de datos, handlers de eventos). Una vez tipeados los bordes, muchos errores internos de any desaparecen porque TypeScript puede inferir más.
Los errores por null y undefined suelen necesitar menos trabajo del que parecen. Prefiere el narrowing sobre tipos complejos: comprueba el valor, retorna temprano y mantén el camino feliz limpio. Cuando un valor por defecto es aceptable, ponlo una vez (por ejemplo, name ?? "" para mostrar) en lugar de esparcir aserciones no nulas (!) por todas partes.
Cuando manejes JSON generado por IA, usa unknown en lugar de any. unknown obliga a comprobar antes de usar el valor, que es exactamente lo que quieres para payloads no confiables.
Arreglos prácticos que puedes aplicar rápido
- Tipa entradas y salidas primero (funciones de API, utilidades, componentes), luego rellena internamente solo donde queden errores.
- Reduce valores nulos con
if (!value) return ...y guards pequeños comotypeof x === "string". - Usa
unknownpara JSON y resultados de parseo, y luego afina con comprobaciones simples antes de leer propiedades. - Para respuestas de API, valida o estrecha solo lo que necesitas en lugar de confiar en toda la forma del payload.
React y formularios son puntos dolor comunes. Tipa eventos (React.ChangeEvent<HTMLInputElement>), mantén consistentes los tipos de estado y define explícitamente las props de los componentes. Un tipo correcto de evento puede eliminar sorprendentemente muchos errores downstream.
Guardarraíles: evita que la strictness se afloje
Activar la strictness es solo la mitad del trabajo. La parte difícil es mantenerla cuando la próxima “fix rápida” resulta tentadora, especialmente en código generado por IA donde es fácil parchear un problema con any.
Fija guardarraíles que hagan el camino seguro el más fácil. No necesitas un gran conjunto de reglas. Empieza con unas pocas políticas que reflejen cómo tu equipo suele romper tipos:
- lint contra
anyy aserciones de tipo “inseguras” (as unknown as X) salvo en unos pocos archivos permitidos - marca promesas flotantes para que no se ignoren errores async
- exige tipos de retorno explícitos en límites clave (handlers de API, auth, acceso a datos)
- prohíbe
// @ts-ignoresalvo que incluya una razón y una fecha de caducidad - vigila soluciones alternativas a
noUncheckedIndexedAccessque oculten casos nulos reales
Mantén las excepciones visibles. Si un archivo necesita quedarse suelto por una semana, márcalo y hazle seguimiento en lugar de dejar que lo “temporal” se vuelva permanente.
Añade una puerta simple en CI: los builds fallan si aparecen nuevos errores de TypeScript. Durante una migración por etapas, la versión más fácil es acotar las comprobaciones a la carpeta que estás endureciendo primero y luego ampliar el alcance con el tiempo.
También puedes añadir “type tests” ligeros para formas que importan. Son comprobaciones en tiempo de compilación que protegen estructuras de datos clave, como “una Session debe incluir un userId” o “la respuesta de la API para /me incluye email y role.” Manténlos pequeños.
Finalmente, reduce duplicación creando una pequeña carpeta de tipos compartidos para formas que la app reinventa: User, Session, Role, ApiError. Cuando estos tipos viven en un solo lugar, el trabajo de strictness se mantiene consistente y el código generado futuro tiene un objetivo claro que igualar.
Errores comunes que estancan las migraciones (y cómo evitarlos)
Los proyectos en strict-mode se estancan cuando el equipo apaga el compilador pero no confía en el comportamiento en tiempo de ejecución. El objetivo no es “hacer que los errores desaparezcan”, es reducir bugs en producción mientras endureces los tipos.
La forma más rápida de perder esa confianza es silenciar al compilador en lugar de arreglar la causa.
Atajos que salen caro
- Sustituir arreglos reales por aserciones
as. Si escribesas anyoas SomeTypecon frecuencia, para y pregunta qué puede ser el valor en tiempo de ejecución. Valida o estrecha. - Usar la aserción no nula (
!) por todas partes. Convierte advertencias en crashes en tiempo de ejecución, sobre todo alrededor de estado de auth, datos asincrónicos y vars opcionales de entorno. - Construir tipos unión gigantes para modelar entradas desordenadas. Pueden ser “correctos”, pero si nadie puede leerlos, se pudren. Normaliza los datos en el límite y usa una forma interna limpia.
- Cambios de tipado que también cambian el comportamiento. Edits pequeños como intercambiar
||por??, cambiar defaults o reordenar comprobaciones pueden alterar salidas. Mantén los cambios de tipado separados de cambios lógicos para que las revisiones sean claras. - Migrar todo en un PR masivo. Parece eficiente hasta que un archivo difícil bloquea toda la fusión. Divide el trabajo en rebanadas que puedas acabar.
Una práctica útil: cada vez que añadas un cast o !, déjate una nota y trata de eliminarlo antes del siguiente hito. Si no puede eliminarse, probablemente necesites una comprobación real en tiempo de ejecución.
Un ejemplo rápido
Imagina un flujo de registro generado por IA que lee req.body.email y silencia errores con as string y email!.trim(). Compila, pero un payload vacío ahora lanza. Una solución más segura es comprobar el tipo y devolver un error claro temprano.
Checklist rápido antes de activar strict a mayor escala
Antes de ampliar las opciones estrictas por el repo, asegúrate de que lo básico esté estable. El modo estricto cambia cuánto puedes confiar en cada valor que fluye por la app.
Si el proyecto solo compila en un portátil, o depende de una versión de TypeScript sin fijar, perderás tiempo persiguiendo errores que no tienen nada que ver con tu código.
Comprobaciones pre-vuelo que hacen los despliegues previsibles:
- La app compila hoy limpiamente con una versión de TypeScript fijada y con el mismo comando en CI.
- Puedes nombrar las 2 o 3 fuentes principales de bugs en tiempo de ejecución (a menudo valores nulos, desajustes en respuestas de API y casos límite de auth) y sabes dónde aparecen.
- Tus límites principales están tipeados, aunque el interior siga siendo desordenado: llamadas del cliente de API, capa de acceso a datos y variables de entorno.
- Puedes mantener cambios pequeños: cada PR se completa en menos de un día y tiene un tema claro.
- Tienes una regla clara sobre cuándo
anyes aceptable, y es rara y documentada.
Un escenario simple ayuda: imagina que tu prototipo a veces cierra la sesión de usuarios tras un refresh. Si tu helper de auth devuelve User | null pero el código downstream lo trata como siempre presente, obtendrás crashes aleatorios. Antes de expandir la strictness, confirma que tienes un lugar tipeado donde “user puede ser null” y el resto de la app lee desde ahí.
Ejemplo: endurecer un prototipo sin frenar el trabajo de features
Un fundador hereda un prototipo de Lovable o Bolt. Funciona bien localmente, pero en producción se estrella después del login y algunas páginas muestran datos en blanco. El código “funciona” hasta que un usuario real toca un caso límite: un campo ausente, un null desde la API o un número que llega como string.
En lugar de poner strict: true por todas partes y ahogarte en errores, trata el modo estricto como una serie de arreglos pequeños y de alto impacto.
Empieza donde los bugs en tiempo de ejecución duelen más: la capa de API y auth. En este prototipo, getSession() a veces devuelve undefined, pero la UI asume que siempre existe. Las comprobaciones estrictas lo sacan rápido a la luz.
// Before
const userId = session.user.id
// After
const userId = session?.user?.id
if (!userId) throw new Error("Not authenticated")
Después, pasa a utilidades compartidas, donde un arreglo puede proteger muchas pantallas. Un ejemplo común es el parseo numérico. La API envía "42", pero la app lo trata como número y luego hace operaciones que devienen en NaN.
Un camino por etapas que suele mantener el trabajo de features en marcha:
- añade strictness solo a archivos de API/auth primero
- arregla los errores principales que mapean a crashes reales
- endurece helpers compartidos (parseo, manejo de fechas, config)
- expande las comprobaciones estrictas a componentes UI al final
En el camino te encontrarás con errores típicos de código generado: asumir que campos opcionales siempre existen (profile.name), mezclar tipos (string | number) o devolver objetos parciales. La solución rara vez es compleja. Añade guards simples, corrige tipos de retorno y normaliza datos en el límite (respuesta API dentro, tipos limpios de app fuera).
¿Cómo se ve “bien” tras el despliegue? Menos crashes en producción, reglas más claras sobre qué puede ser undefined y un pequeño grupo de responsables para archivos clave (auth, cliente API, tipos compartidos).
Próximos pasos: ten un plan de migración claro (y pide ayuda si la necesitas)
El modo estricto funciona cuando eliges un ritmo que puedas mantener. Antes de comprometerte, decide qué significa “hecho” para tu app: menos bugs en producción, auth y manejo de datos más seguros o una base de código que el equipo pueda cambiar sin miedo.
Si sigues viendo los mismos bugs “imposibles” (cierres de sesión aleatorios, lógica de auth que cambia entre entornos, secretos en el repo), eso es señal de que los límites necesitan atención. Otra señal es arquitectura espagueti: archivos que hacen de todo, sin separación clara, y arreglos de tipos que se extienden por docenas de módulos no relacionados.
Si heredaste un prototipo generado por IA y quieres una mirada externa, FixMyMess (fixmymess.ai) se centra en tomar apps construidas por IA y convertirlas en software listo para producción diagnosticando la base de código, reparando lógica, endureciendo seguridad, refactorizando áreas inseguras y preparando despliegues. Un primer paso práctico es una auditoría de código gratuita para obtener un plan por etapas del modo estricto sin adivinar por dónde empezar.
Preguntas Frecuentes
¿Debo activar `"strict": true` de una vez o habilitar las banderas una a una?
Activa "strict": true para habilitar todo el paquete, pero en código desordenado suele ser más seguro activar las banderas individuales una por una. Comienza por las comprobaciones que obligan a claridad en los límites y luego expande cuando la cantidad de errores sea manejable.
¿Por qué las apps TypeScript generadas por IA explotan con errores cuando se activa el modo estricto?
Esperarás muchos errores donde la app está haciendo suposiciones: respuestas de API tipadas como any, campos opcionales usados como si siempre existieran y objetos de auth/sesión pasados sin una forma clara. El compilador está señalando las mismas suposiciones que causan fallos en tiempo de ejecución como “cannot read property of undefined”.
¿Cuál es el mejor lugar para empezar un despliegue del modo estricto?
Empieza en los límites de ejecución: código del cliente de la API, parseo de request/respuesta, helpers de auth/sesión, acceso a variables de entorno y lecturas de base de datos. Tipar y manejar null ahí suele eliminar clases enteras de errores downstream sin tocar cada archivo de UI.
¿Cuándo debo usar `unknown` en lugar de `any`?
Usa unknown para JSON y cargas no confiables para forzarte a comprobar antes de leer propiedades. Usa any solo como escape temporal cuando estés bloqueado, y regístralo para que no se propague por la base de código.
¿Cómo arreglo “Object is possibly undefined” sin llenar el código de `!`?
Prefiere el estrechamiento (narrowing) con pequeñas comprobaciones en tiempo de ejecución y retornos tempranos, manteniendo el “camino feliz” limpio. Si un valor por defecto es válido, asígnalo una vez donde entra en la UI o la capa de dominio en lugar de poner ! por todas partes, porque las aserciones no nulas pueden ocultar fallos reales.
¿Cómo puedo migrar a strict sin detener el trabajo de features?
Estratifica la migración por carpeta, feature, paquete o punto de entrada, y mantén cada cambio lo bastante pequeño para terminar rápido. Fusiona a menudo para evitar una rama larga de “modo estricto” que se vuelva imposible de revisar o rebasar.
¿Qué banderas de strict debo activar primero y en qué orden?
Un orden práctico es noImplicitAny primero para evitar tipado débil silencioso, luego strictNullChecks para atrapar fallos por null/undefined, y después añadir banderas más focalizadas tras aprender los patrones de error del código. La clave es arreglar tras cada paso antes de avanzar.
¿Cómo reduzco rápido los errores de implicit any sin sobretipar todo?
Tipa entradas y salidas primero: parámetros de funciones, tipos de retorno y límites de módulo como helpers de API y acceso a datos. Cuando los bordes están tipeados, TypeScript infiere más dentro del módulo y muchos errores desaparecen sin tener que tipar cada variable local.
¿Cuáles son los errores comunes en migraciones a strict que causan regresiones?
Separa cambios de tipado de cambios de comportamiento, porque ediciones “pequeñas” pueden alterar la lógica sutilmente. Evita casts repetidos como as SomeType y as any; si los necesitas con frecuencia, añade una comprobación real en tiempo de ejecución o normaliza los datos en el límite.
¿Cómo evito que el modo estricto vuelva a relajarse después de la migración?
Añade una comprobación CI simple que falle si aparecen nuevos errores de TypeScript en el área que estás endureciendo, y luego amplia el alcance con el tiempo. Si heredaste un prototipo generado por IA y quieres un plan por etapas verificado por humanos, FixMyMess puede empezar con una auditoría de código gratuita y normalmente estabilizar proyectos en 48–72 horas.