TypeScript y agentes IA: por qué el lenguaje que eliges importa
El lenguaje que usas afecta cómo razona tu IA. Tres mecanismos concretos por los que TypeScript mejora el código generado por agentes como Claude Code.
Colaboradores: Ivan Garcia Villar
Hace unos meses empecé a notar un patrón revisando las iteraciones de Claude Code en proyectos con TypeScript estricto: solucionaba los problemas más rápido. Menos correcciones, menos loops, menos veces tenía que intervenir para reorientar al agente. No era cuestión del prompt ni del modelo, sino del entorno donde operaba.
Se habla mucho de qué modelo usar, cómo estructurar los prompts, RAG, ventanas de contexto. Pero hay algo anterior a todo eso que afecta la calidad del código que genera tu IA: el lenguaje en el que está escrito tu proyecto. Te lo explico en cuatro mecanismos concretos.
Compilados vs. no compilados. La calidad del feedback no es igual en todos los lenguajes
Los agentes como Claude Code operan en loops. Generan código, reciben feedback, corrigen, repiten. La velocidad a la que convergen hacia una solución correcta depende de la calidad de ese feedback.
Cuando Claude Code genera código incorrecto en un proyecto TypeScript, el compilador describe el error con precisión. Propiedad inexistente en tal tipo. Función que espera un string y recibe un number. Devuelve el error exacto, localizado, sin ambigüedad. Es retroalimentación directa.
Un estudio publicado en el GitHub Blog en enero de 2026 encontró que el 94% de los errores de compilación generados por LLMs son fallos de type-checking [1]. No es un dato menor: significa que casi todos los errores que el compilador detecta sobre código generado por IA son exactamente la categoría que TypeScript intercepta antes de runtime.
Compara eso con el equivalente en JavaScript. El mismo bug aparece como un TypeError: Cannot read properties of undefined ejecutado tres pasos después del error real, en una línea que no tiene nada que ver con la causa original. El agente tiene que rastrear hacia atrás, especular, volver a intentar.
// TypeScript: el error se localiza en la causa, no en la consecuencia
function formatProduct(product: Product | null): string {
// Error TS2531: Object is possibly 'null'
// El agente recibe instrucción exacta: necesita un guard para el caso null
return product.name.toUpperCase();
}
// JavaScript: el error aparece en runtime, lejos de la causa real
function formatProduct(product) {
// TypeError: Cannot read properties of null (reading 'name')
// ocurre aquí, pero la causa puede estar en quien llamó la función
// El agente tiene que rastrear hacia atrás para encontrar el origen
return product.name.toUpperCase();
}
No es que la IA sea más inteligente con TypeScript. Es que el entorno le da mejores pistas. Y la calidad del feedback determina la calidad de la corrección.
Tu código es tu mejor documentación. Cuanto mas exacta, mejor.
Cuando le pides a un agente que implemente una función, que modifique un endpoint o que integre un servicio nuevo, el modelo no trabaja desde cero. Lee el código existente para entender qué convenciones sigues, qué tipos de datos manejas, qué contratos existen entre módulos. Desde ahí construye. Todo tu código es su contexto, y todo el contexto influye en el resultado.
En un proyecto JavaScript, lo que ve son variables con nombres, comentarios si tienes suerte, y patrones de uso. Tiene que suponer si score es un número entre 0 y 1 o puede ser cualquier cosa. Tiene que asumir si id es un string, un número o un UUID. Tiene que adivinar la forma de los objetos que viajan entre funciones.
Con TypeScript, no adivina. Lee.
Una interfaz bien diseñada comunica en ocho líneas lo que de otro modo requeriría un párrafo de instrucciones en el prompt. Tus interfaces, los tipos personalizados, los genéricos con restricciones no son información con alto significado semántico que el modelo procesa cuando construye el contexto de lo que tiene que generar.
// Tipos de dominio: especificaciones que el modelo lee, no asume
type UserId = string & { readonly __brand: 'UserId' };
type OrderId = string & { readonly __brand: 'OrderId' };
type Cents = number & { readonly __brand: 'Cents' };
interface Order {
id: OrderId;
userId: UserId;
totalCents: Cents; // nunca "amount" ambiguo — siempre centavos
status: OrderStatus; // unión discriminada, no string libre
createdAt: Date;
}
type OrderStatus =
| { type: 'pending' }
| { type: 'confirmed'; confirmedAt: Date }
| { type: 'shipped'; trackingId: string }
| { type: 'delivered'; deliveredAt: Date };
La implicación es directa: cada interfaz que defines, cada tipo de dominio que creas en lugar de usar un string genérico, mejora en silencio la calidad del código que va a generar tu IA en ese módulo.
| Aspecto | JavaScript | TypeScript estricto |
|---|---|---|
| Feedback de error | Ambiguo, en runtime | Preciso, antes de runtime |
| Contexto disponible para el modelo | Nombres de variables, comentarios | Tipos, interfaces, contratos explícitos |
| Propagación de cambios de contrato | Búsqueda manual y revisión visual | Errores de compilación en todos los consumidores |
| Refactors globales | Alto riesgo, difícil de verificar | Lista verificable de puntos afectados |
| Código generado sobre entidades clave | Asume contratos implícitos | Lee contratos explícitos del código |
Cargando ejercicio...
Hay una diferencia fundamental entre cómo un desarrollador resuelve la ambigüedad y cómo lo hace un agente.
Tú, cuando no entiendes el contrato de una API, preguntas. Buscas en el canal de Slack, lees la PR donde se introdujo ese endpoint, le preguntas al compañero que lo escribió. Tienes acceso a contexto social e histórico que no está en el código. El modelo no tiene nada de eso. Trabaja desde lo que puede leer.
En proyectos con un monorepo TypeScript bien estructurado, cuando el backend cambia la forma de un payload, los errores de tipos se propagan automáticamente a todos los consumidores. El agente ve esa cadena de errores y entiende el alcance del cambio sin que nadie tenga que explicárselo.
// packages/shared/src/types/payment.ts
// Un cambio aquí: el compilador marca todos los consumidores automáticamente
export interface PaymentResult {
success: boolean;
transactionId: TransactionId;
amountCents: Cents; // renombrado de `amount` — el agente ve el impacto total
currency: 'EUR' | 'USD'; // campo nuevo — cada consumidor debe manejarlo
}
// El agente recibe una lista verificable de puntos afectados:
// TS2339 en api/payments/controller.ts:34 — result.amount → result.amountCents
// TS2339 en frontend/checkout/Summary.tsx:67 — misma corrección
// TS2741 en workers/invoice/generator.ts:12 — falta currency en el template
Los refactors globales que en un proyecto JavaScript requieren búsquedas de texto y revisión manual se convierten en operaciones que el agente puede ejecutar con una lista verificable de puntos afectados. No porque el modelo sea más capaz. Porque el sistema de tipos le da el mapa.
A esto se le llama “contextualización estática”: el modelo usa el grafo de tipos para derivar contexto antes de generar, en lugar de especular sobre los contratos entre módulos. El repositorio deja de ser un conjunto de archivos y se convierte en un grafo de conocimiento que el agente puede recorrer.
Lo que cambia si lo tomas en serio
La mayoría de los consejos sobre cómo trabajar mejor con IA agéntica apuntan al prompt: sé más específico, da más contexto, usa system prompts elaborados. Todo eso ayuda. Pero hay una capa anterior que se ignora con demasiada frecuencia: la calidad estructural del entorno en el que opera el agente.
TypeScript estricto, interfaces de dominio bien diseñadas, branded types para las entidades clave del sistema. No son overhead burocrático. Son la especificación que el agente lee en lugar de adivinar.
Cada vez que defines un tipo en lugar de usar any, reduce el espacio de errores en el que puede caer la IA. Cada interfaz explícita entre módulos es un contrato que el compilador verifica y el modelo consume. Cada monorepo bien estructurado es un mapa que el agente puede seguir sin que le expliques la arquitectura en cada sesión.
La consecuencia práctica es que el tiempo que inviertes en diseñar buenos tipos de dominio no lo recuperas solo en mantenimiento humano. Lo recuperas también en iteraciones del agente. Una sesión de Claude Code en un proyecto con strict: true y tipos de dominio bien pensados requieren menos intervenciones, producen menos código que hay que descartar y convergen en menos iteraciones.
No es magia. Es estructura. Los compiladores llevan décadas dependiendo de que el código sea explícito. Los agentes de codificación también.
Errores comunes
TypeScript como formalidad: any generalizado
El error más frecuente es migrar a TypeScript sin comprometerse con él. Un proyecto con strict: false en el tsconfig y any en la mitad de los parámetros de función da al compilador algo menos que JavaScript: la misma ambigüedad, con más ruido. El agente ve any y entiende correctamente que no hay contrato que respetar. Configura strict: true desde el principio y activa un lint rule que marque any explícito en código de producción. Si no puedes hacer eso, el proyecto no está listo para sacar provecho de este mecanismo.
Tipos estructurales sin semántica
id: string en lugar de id: UserId parece una distinción pedante. No lo es. Cuando el modelo ve tres funciones que aceptan string, no puede saber cuál espera un UserId, cuál un email y cuál un nombre libre. Cuando ve UserId, Email y DisplayName como branded types distintos, entiende los contratos sin que nadie se los explique. El coste de definir el branded type es de cinco líneas. El beneficio se acumula en cada función que el agente genera sobre esa entidad.
Interfaces solo para los objetos “importantes”
Los equipos suelen tipar bien los modelos de dominio pero dejan los objetos internos (configuración, parámetros de utilidades, respuestas intermedias) como objetos literales o Record<string, unknown>. El resultado es un mapa incompleto: el agente entiende bien la frontera del sistema pero especula en el interior. La coherencia importa. Un repositorio parcialmente tipado da al modelo información parcial, y los errores tienden a concentrarse exactamente en las partes no tipadas.
Comentarios en lugar de tipos
// MAL: el agente lee el comentario, pero no puede verificar que el código lo cumpla
// userId debe ser un UUID v4 válido
function getUser(userId: string): Promise<User> { ... }
// BIEN: el compilador verifica el contrato, el agente lo lee en los tipos
function getUser(userId: UserId): Promise<User> { ... }
Un comentario describe una intención. Un tipo es una restricción verificable. Para el agente y para el compilador, la diferencia es absoluta: solo la restricción puede generar un error cuando se viola.
Checklist de implementación
-
strict: trueconfigurado entsconfig.jsondesde el inicio del proyecto -
Lint rule que marque
anyexplícito en código de producción (@typescript-eslint/no-explicit-any) -
Branded types para las entidades clave del dominio: IDs, monedas, unidades de medida etc
-
Interfaces explícitas en los contratos entre módulos del monorepo
-
DTOs de API tipados en el servidor y valorar exportados al cliente desde un paquete compartido si no se quiere mantener por duplicado
-
El compilador corre en cada commit a través de triggers automáticos para detectar los problmeas lo antes posible y que el model aún tenga contexto de los cambios realizados
-
Sin objetos literales sin tipar en los contratos entre funciones de dominio
En monorepos grandes, el tiempo de compilación puede añadir latencia al loop del agente — tsc --watch y builds incrementales mitigan esto sin sacrificar los beneficios.
Fuentes
- Why AI is pushing developers toward typed languages — GitHub Blog, Cassidy Williams, enero 2026 — Dato del 94% de errores de compilación LLM como fallos de type-checking; posición de TypeScript como lenguaje más usado en GitHub en agosto de 2025.
- Statically Contextualizing Large Language Models with Typed Holes — OOPSLA 2024, PACMPL Vol. 8 — Demostración empírica de que inyectar información estática de tipos mejora la precisión del código generado; propuesta de extensión ChatLSP para el Language Server Protocol.
- AgenticTyper: Automated Typing of Legacy Software Projects Using Agentic AI — ICSE 2026 SRC — 633 errores de tipos resueltos en 20 minutos en dos repositorios de 81.000 líneas; trabajo equivalente estimado en un día completo para un desarrollador senior.
- Type-Constrained Code Generation with Language Models — arXiv, abril 2025 — Reducción de errores de compilación a más de la mitad mediante prefix automata y restricciones de tipo durante la decodificación; evaluado en HumanEval y MBPP.
Preguntas Frecuentes
¿Esto aplica solo a TypeScript, o también a Go, Rust o Java?
El mecanismo es general a cualquier lenguaje con tipos estáticos expresivos. Go tiene interfaces estructurales y tipos claros; Rust tiene un sistema de tipos especialmente estricto con ownership que genera errores altamente localizados. Lo que hace a TypeScript especialmente relevante en este contexto es su adopción masiva en el ecosistema web y en la mayoría de los proyectos de agentes (Node.js backends, frontends, CLIs). Los principios aplican a otros lenguajes: errores localizados antes de runtime, contratos explícitos que el modelo puede leer, propagación automática de cambios de contrato.
¿Vale la pena migrar un proyecto JavaScript existente a TypeScript solo por los agentes?
Depende del tamaño del proyecto y de qué tan intensamente usas agentes en él. Para un proyecto pequeño con poco uso agéntico, probablemente no justifica el esfuerzo de migración. Para un monorepo grande donde el equipo usa Claude Code o herramientas similares a diario, el retorno es real: menos iteraciones de corrección, refactors más seguros, menor supervisión necesaria por sesión. El punto clave es que una migración superficial, con any generalizado y strict: false, no da los beneficios descritos aquí. Si migras, hazlo con una estrategia real o no lo hagas.
¿Qué pasa si uso any ampliamente en TypeScript? ¿Sigue ayudando?
No en los aspectos que importan para los agentes. any desactiva el type-checker en ese punto: el compilador no genera errores de tipo, el modelo no recibe feedback preciso, y los contratos que el agente podría leer dejan de existir. Tienes el overhead de TypeScript sin sus beneficios. Un proyecto con any generalizado da al agente aproximadamente la misma información que JavaScript, con la diferencia de que el compilador no genera errores útiles porque any hace que todo sea compatible con todo.
¿Cómo afecta esto a Python con type hints?
Python con type hints y mypy configurado en modo estricto se acerca al mecanismo descrito aquí, pero con diferencias importantes. Los type hints en Python son opcionales por diseño: el runtime los ignora, y el type-checker solo opera si está configurado y se ejecuta explícitamente. Esto significa que el feedback al agente es menos inmediato que con un compilador integrado en el ciclo de desarrollo. Dicho esto, proyectos Python con type hints consistentes y mypy en CI capturan la misma categoría de errores antes de runtime, y los estudios citados sobre información de tipos en prompts aplican igualmente. La diferencia es de integración en el toolchain, no de principio.