TypeScript es un lenguaje que solo existe en tiempo de compilación. Node.js no tiene un intérprete de TypeScript en su runtime, por lo que cada archivo .ts que un proceso de Node "ejecuta" ya se ha convertido en JavaScript plano antes de que V8 lo vea.
Ese único hecho replantea la mayor parte de lo que cubre esta sección: Conceptos básicos de TypeScript en Node muestra configuraciones tsconfig.json funcionales, y tsx vs tsc vs ts-node compara herramientas específicas, pero ninguna responde a la pregunta subyacente a ambas: qué hace realmente una "cadena de herramientas de TypeScript" y por qué las piezas son separables. Esta página es ese modelo mental, el marco sobre el que se construye el resto de la sección.
TypeScript en Node es una capa en tiempo de compilación atornillada a un runtime que solo entiende JavaScript, por lo que cada elección de cadena de herramientas es en realidad una elección sobre cuándo y cómo se borran los tipos.
Por qué es importante: Los errores de tipo, las discrepancias en la resolución de módulos y las sorpresas de "funciona en desarrollo pero no en producción" casi siempre se remontan a dos herramientas que no están de acuerdo sobre cómo el código fuente se convierte en JavaScript.
Conceptos clave:borrado de tipos, verificación de tipos, transpilación, resolución de módulos, mapas de origen, archivos de declaración.
Cuándo usarlo: Al elegir una cadena de herramientas de desarrollo frente a producción, depurar por qué un error de tipo no detuvo una implementación, decidir una estrategia module/moduleResolution de tsconfig.json, o incorporar un equipo acostumbrado a un flujo de trabajo de TypeScript primero en el navegador.
Limitaciones / Compensaciones: Las transformaciones rápidas en desarrollo compran velocidad al omitir la verificación de tipos, lo que significa que una compilación rota aún puede ejecutarse localmente hasta que CI o tsc la detecten.
Temas relacionados: resolución de módulos, tipado gradual, validación en tiempo de ejecución en los límites, eliminación de tipos nativa de Node.
Cada flujo de trabajo de TypeScript realiza dos tareas lógicamente separadas: la verificación de tipos, que verifica tu código contra los tipos que escribiste e informa errores, y la transpilación (o "compilación"), que elimina esos tipos y emite JavaScript ejecutable.
Nada requiere que la misma herramienta haga ambas cosas, y en el ecosistema de Node, rutinariamente no lo hacen. tsc, el compilador oficial de TypeScript, puede hacer ambas cosas a la vez, pero muchos equipos lo usan solo para la verificación de tipos (tsc --noEmit) y dejan que una herramienta separada y más rápida maneje la salida real de JavaScript.
Esa separación existe porque la verificación de tipos es comparativamente lenta (tiene que construir una comprensión completa de los tipos de tu programa), mientras que la eliminación de anotaciones es comparativamente barata, más cercana a eliminar texto que a analizarlo. Una analogía útil: la verificación de tipos es un corrector de pruebas que valida el argumento de un ensayo, mientras que la transpilación es un mecanógrafo que elimina tus notas editoriales al margen antes de imprimir. Puedes contratar a dos personas diferentes para esos trabajos, y la mayoría de las configuraciones rápidas de desarrollo de Node hacen exactamente eso: una transformación ligera (tsx, esbuild, SWC) elimina los tipos sobre la marcha para mayor velocidad, mientras que tsc o el servicio de lenguaje de tu editor realizan la corrección de pruebas real, a menudo en un horario separado y más lento.
function greet(name: string): string { return `Hello, ${name}`;}// Después del borrado, Node solo ejecuta:// function greet(name) { return `Hello, ${name}`; }
Tres preocupaciones distintas interactúan cada vez que el código TypeScript llega a un proceso de Node, y confundirlas es donde comienza la mayor parte de la confusión.
La verificación de tipos se realiza contra los tipos, no contra los valores en tiempo de ejecución; solo puede detectar lo que describen tus anotaciones y no tiene ningún efecto sobre el comportamiento del JavaScript emitido. Una ejecución de tsc --noEmit que falla aún deja tu salida dist/ intacta; la verificación de tipos es puramente consultiva a menos que algo (CI, un hook de git, tu editor) esté configurado para bloquearla.
La resolución de módulos es donde el mundo en tiempo de compilación de TypeScript debe estar de acuerdo con las reglas de carga en tiempo de ejecución de Node, y esta es una fuente frecuente de fricción porque los dos evolucionaron de forma algo independiente. La configuración moduleResolution: "NodeNext" de TypeScript le dice al compilador que resuelva las importaciones utilizando las mismas reglas que Node usa en tiempo de ejecución, respetando el campo "type" de package.json, requiriendo extensiones de archivo explícitas en las importaciones de estilo ESM y respetando los mapas de exports, para que lo que se verifica en tiempo de compilación localmente también se resuelva cuando Node carga el JavaScript emitido.
Los archivos de declaración (.d.ts) solo contienen información de tipo, nunca código en tiempo de ejecución, que es cómo un paquete npm publicado puede enviar tipos sin enviar código fuente de TypeScript en absoluto: el archivo .d.ts describe la forma, el archivo .js emparejado es lo que Node realmente ejecuta.
// tsconfig.json (extracto) - esta única configuración decide si// la resolución en tiempo de compilación de TypeScript coincide con las reglas de tiempo de ejecución de Node{ "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext" }}// Las configuraciones no coincidentes aquí son la razón por la que una importación puede verificar tipos// limpiamente pero arrojar "Cannot find module" en el momento en que Node la ejecuta.
Node 24 añade una complicación adicional que vale la pena nombrar con precisión: puede ejecutar archivos .ts directamente eliminando anotaciones de tipo simples en el momento de la carga, sin un paso de compilación separado. Esto es borrado de tipos integrado en el cargador de módulos del runtime, no verificación de tipos: Node elimina las anotaciones que reconoce y ejecuta lo que queda; no verifica que tus tipos sean consistentes y rechaza la sintaxis de TypeScript que requiere una transformación real (como enum o la fusión de espacios de nombres) en lugar de una simple eliminación. tsx vs tsc vs ts-node cubre cómo se compara esto con las herramientas de desarrollo dedicadas con las que se superpone parcialmente.
Debido a que la verificación y la emisión son separables, los equipos terminan eligiendo diferentes herramientas para diferentes momentos en el ciclo de vida de un proyecto, y cada elección intercambia velocidad por garantías.
Enfoque
Fortaleza
Debilidad
Mejor ajuste
tsc (compilar + verificar)
Única fuente de verdad; detecta cada error de tipo antes de la emisión
Opción más lenta; no está diseñado para bucles de desarrollo iterativos rápidos
Paso de compilación de producción, puerta de CI
Transformación rápida (tsx, esbuild, SWC)
Inicio casi instantáneo; excelente ergonomía en modo de desarrollo/vigilancia
No realiza verificación de tipos: los tipos rotos aún se ejecutan
Desarrollo local, ejecutores de pruebas
Eliminación de tipos nativa de Node
Cero herramientas para scripts simples; nada que instalar
Solo borrado, y rechaza anotaciones que requieren una transformación real
Scripts, prototipos, servicios simples sin características complejas de TS
Servicio de lenguaje del editor
Retroalimentación continua e integrada mientras escribes
Tan preciso como los archivos/configuración abiertos del proyecto; no es una puerta de compilación
Autoría diaria, no aplicación de CI
Esta división tiene consecuencias arquitectónicas reales. Un servicio puede pasar todas las ejecuciones de pruebas locales con tecnología tsx mientras envía un error de tipo directamente a producción, si nada en el pipeline llama a tsc en modo de verificación, por lo que la mayoría de las configuraciones maduras de Node/TypeScript ejecutan una transformación rápida para la velocidad de iteración y un paso dedicado tsc --noEmit en CI, en lugar de confiar solo en una de ellas.
El mismo principio de borrado se extiende más allá del límite del lenguaje: debido a que los tipos desaparecen en tiempo de ejecución, TypeScript puede describir la forma que esperas de un cuerpo de solicitud, una variable de entorno o una respuesta de API de terceros, pero no puede verificar que esa forma realmente llegó; eso es una preocupación en tiempo de ejecución, no en tiempo de compilación. Zod en los límites cubre el emparejamiento de tipos en tiempo de compilación con la validación en tiempo de ejecución exactamente en esos límites, y Compartir tipos con el frontend cubre el problema relacionado de mantener un contrato en tiempo de compilación sincronizado entre dos procesos implementados por separado.
"Node ejecuta TypeScript." Node ejecuta JavaScript; cada ruta desde el código fuente .ts hasta un proceso en ejecución pasa primero por un paso de borrado o compilación, incluso cuando ese paso es invisible (como la eliminación integrada de Node 24).
"Si tsx ejecuta mi archivo sin errores, mis tipos son correctos."tsx y transformaciones similares eliminan tipos sin verificarlos; un archivo con errores de tipo reales aún puede ejecutarse limpiamente bajo una transformación rápida.
"La resolución de módulos de TypeScript simplemente refleja cómo Node carga archivos." Se implementan de forma independiente y pueden estar en desacuerdo; moduleResolution: "NodeNext" existe específicamente para hacer que el modelo del compilador coincida con el comportamiento en tiempo de ejecución de Node en lugar de asumirlo.
"Los archivos de declaración (.d.ts) contienen código en tiempo de ejecución." Describen solo tipos; un paquete puede enviar archivos .d.ts sin ningún código fuente de TypeScript correspondiente, puramente para describir una API JavaScript ya compilada.
"La eliminación de tipos nativa de Node significa que ya no necesito tsc." La eliminación borra anotaciones; no las valida y no puede manejar características de TypeScript que requieren una transformación de código real, por lo que un paso real de verificación de tipos sigue siendo necesario para las garantías de corrección.
¿Por qué Node.js no soporta TypeScript directamente?
TypeScript es un superconjunto de JavaScript definido completamente por su sistema de tipos, y los tipos son un concepto solo en tiempo de compilación; no queda nada para que un motor JavaScript como V8 "ejecute" una vez que se eliminan los tipos, por lo que construir soporte para TypeScript en Node significaría construir un compilador en el runtime en lugar de agregar una nueva capacidad de ejecución.
¿Cuál es la diferencia real entre la verificación de tipos y la transpilación?
La verificación de tipos analiza tu código contra sus tipos declarados e informa las discrepancias sin cambiar la salida; la transpilación elimina esos tipos (y reduce la sintaxis más nueva si es necesario) para producir JavaScript ejecutable, independientemente de si la verificación pasó.
¿Puedo ejecutar TypeScript en Node sin ningún paso de compilación?
Sí, de dos maneras: una transformación rápida de desarrollo como tsx borra los tipos sobre la marcha sin una compilación separada, o la eliminación de tipos nativa de Node 24 hace lo mismo para anotaciones simples sin ninguna herramienta instalada; ninguna de las dos verifica los tipos de tu código.
¿La eliminación de tipos integrada de Node reemplaza a `tsc`?
No, solo elimina la sintaxis de tipos que reconoce como segura de borrar; no valida los tipos y produce errores en las características de TypeScript que necesitan una transformación real (enums, namespace, etc.), por lo que complementa a tsc para scripts rápidos en lugar de reemplazarlo para proyectos reales.
¿Por qué mi código se verifica localmente pero falla al importar en tiempo de ejecución?
Generalmente, una discrepancia en la resolución de módulos: el compilador de TypeScript resolvió la importación utilizando su propia configuración (que puede diferir de las reglas reales de tiempo de ejecución de Node), por lo que el código satisface al verificador de tipos, pero el especificador no se resuelve de la misma manera una vez que el cargador de módulos de Node es el que lo interpreta.
¿Para qué sirven realmente los archivos de declaración (`.d.ts`)?
Permiten que un paquete JavaScript (o uno de TypeScript después de la compilación) describa sus tipos por separado de su código en tiempo de ejecución, de modo que los consumidores obtengan verificación de tipos y autocompletado del editor sin que el paquete necesite enviar o incluso contener código fuente de TypeScript.
¿Es seguro omitir la verificación de tipos en CI si mi editor no muestra errores?
No, el servicio de lenguaje de un editor refleja los archivos actualmente abiertos y el contexto de su propio proyecto, lo que puede diferir de una compilación limpia y desde cero; un paso de CI dedicado tsc --noEmit (o equivalente) es la única puerta confiable porque verifica todo el proyecto de la misma manera cada vez.
¿Por qué algunas configuraciones de Node TypeScript usan dos herramientas diferentes en lugar de solo `tsc`?
Porque tsc hace la verificación y la emisión juntas, y la verificación es la parte lenta; dividir las dos permite que una transformación rápida maneje los ciclos de desarrollo/prueba iterativos, mientras que un paso tsc separado y más lento se ejecuta con menos frecuencia (pre-commit, CI) puramente para la validación.
¿TypeScript alguna vez afecta el comportamiento en tiempo de ejecución de Node?
No, una vez que se borran los tipos, el JavaScript emitido se comporta exactamente como si se hubiera escrito directamente en JavaScript; TypeScript cambia qué errores se detectan antes de la ejecución, no cómo se ejecuta el código una vez que lo hace.
Le dice al compilador de TypeScript que resuelva los especificadores import/require utilizando el mismo algoritmo que Node usa en tiempo de ejecución, respetando el campo "type" de package.json, los mapas de exports y las extensiones explícitas, por lo que una verificación de tipos exitosa es una señal mucho más fuerte de que el JavaScript equivalente realmente se cargará.
Si los tipos se borran, ¿por qué molestarse con TypeScript estricto?
Porque el valor está completamente en el tiempo de autoría: los tipos estrictos detectan una gran clase de errores (manejo de nulos, formas no coincidentes, tipos de argumentos incorrectos) antes de que el código se ejecute, lo que es estrictamente anterior y más barato que detectar los mismos errores en producción.