Estructura de tsconfig.json
El archivo tsconfig.json es la configuración central de cualquier proyecto TypeScript. Indica al compilador tsc que archivos procesar, como transformarlos y que reglas aplicar. Cuando se ejecuta tsc sin argumentos, busca este archivo en el directorio actual y lo utiliza como referencia.

{
"compilerOptions": {
// Opciones del compilador
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.spec.ts"],
"files": ["src/main.ts"]
}
Las secciones principales del archivo son:
- compilerOptions: contiene todas las opciones que controlan el comportamiento del compilador.
- include: patrones glob que determinan que archivos se incluyen en la compilación. Si se omite, incluye todos los archivos
.tsdel proyecto. - exclude: patrones de archivos a excluir. Por defecto, siempre excluye
node_modules. - files: lista explícita de archivos individuales. Raramente se necesita cuando se usa
include.
El archivo tsconfig.json se genera con
tsc --init. El archivo resultante incluye todas las opciones comentadas con descripciones, lo que sirve como referencia completa.
Target y compilación de JavaScript
La opción target determina la versión de JavaScript que genera el compilador. TypeScript transpila la sintaxis moderna (async/await, clases, optional chaining, nullish coalescing) a la versión destino cuando es necesario.
{
"compilerOptions": {
"target": "ES2022"
}
}
Valores comunes y su uso recomendado:
| Target | Entorno recomendado |
|--------|---------------------|
| ES5 | Navegadores muy antiguos (IE11) |
| ES2017 | Node.js 8+ con soporte de async/await |
| ES2020 | Node.js 14+ con optional chaining y nullish coalescing |
| ES2022 | Node.js 16+ con top-level await y class fields |
| ESNext | Siempre la versión más reciente del estándar |
El impacto de target se observa directamente en el código generado:
// Código TypeScript original
const obtenerDatos = async (): Promise<string[]> => {
const respuesta = await fetch("https://api.ejemplo.com/datos");
const datos = await respuesta.json();
return datos;
};
Con target: "ES2022", el código se mantiene prácticamente igual. Con target: "ES5", TypeScript genera código auxiliar para emular async/await con generadores y promesas.
Module y moduleResolution
La opción module determina el sistema de módulos del JavaScript generado. La opción moduleResolution controla como TypeScript resuelve las rutas de importación.
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext"
}
}
Las combinaciones recomendadas dependen del entorno:
| Entorno | module | moduleResolution |
|---------|--------|-----------------|
| Node.js (CommonJS) | CommonJS | Node o Node16 |
| Node.js (ESM) | NodeNext | NodeNext |
| Frontend con bundler | ESNext | Bundler |
La opción Bundler está diseñada para proyectos que usan herramientas como Vite, webpack o esbuild, ya que estos bundlers tienen sus propias reglas de resolución de módulos.
Diferencia práctica entre sistemas de módulos
El sistema de módulos afecta al formato de las sentencias de importación y exportación en el JavaScript generado:
// Código TypeScript
import { formatearFecha } from "./utils.js";
export function procesar(fecha: Date): string {
return formatearFecha(fecha);
}
Con module: "CommonJS":
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("./utils");
function procesar(fecha) {
return (0, utils_1.formatearFecha)(fecha);
}
exports.procesar = procesar;
Con module: "NodeNext" o "ESNext":
import { formatearFecha } from "./utils.js";
export function procesar(fecha) {
return formatearFecha(fecha);
}
Modo estricto y opciones de rigor
La opción strict: true es la más importante para proyectos nuevos. Activa simultáneamente múltiples comprobaciones de seguridad que previenen categorías enteras de errores:
{
"compilerOptions": {
"strict": true
}
}
Lo que activa strict: true:
| Opción | Efecto |
|--------|--------|
| strictNullChecks | null y undefined no son asignables a otros tipos |
| noImplicitAny | Error cuando el tipo se infiere como any |
| strictFunctionTypes | Comprueba la varianza de parámetros de función |
| strictBindCallApply | Tipos correctos para bind, call y apply |
| strictPropertyInitialization | Las propiedades de clase deben inicializarse |
| noImplicitThis | Error cuando this tiene tipo any implícito |
| alwaysStrict | Emite "use strict" en todos los archivos |
| useUnknownInCatchVariables | catch(error) tiene tipo unknown en lugar de any |
Efecto de strictNullChecks
Sin strictNullChecks, cualquier valor puede ser null o undefined sin que el compilador lo detecte:
// Con strictNullChecks: true
function obtenerNombre(id: number): string | null {
if (id === 0) return null;
return "Usuario " + id;
}
const nombre = obtenerNombre(0);
// Error: 'nombre' is possibly 'null'
// console.log(nombre.toUpperCase());
// Solucion: verificar antes de usar
if (nombre !== null) {
console.log(nombre.toUpperCase());
}
Efecto de noImplicitAny
Con noImplicitAny, TypeScript exige anotar los parámetros cuyo tipo no puede inferir:
// Error con noImplicitAny: Parameter 'items' implicitly has an 'any' type
// function procesar(items) { ... }
// Solucion: anotar el tipo
function procesar(items: string[]): void {
items.forEach(item => console.log(item));
}
procesar(["uno", "dos", "tres"]);
Opciones adicionales de rigor
Existen opciones que van más allá de lo que activa strict y proporcionan un nivel de seguridad superior:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"exactOptionalPropertyTypes": true
}
}
La opción noUncheckedIndexedAccess es especialmente útil. Anade undefined al tipo de retorno de los accesos por índice:
const colores = ["rojo", "verde", "azul"];
// Sin noUncheckedIndexedAccess: tipo es string
// Con noUncheckedIndexedAccess: tipo es string | undefined
const color = colores[5];
if (color !== undefined) {
console.log(color.toUpperCase());
}
Opciones de salida y source maps
Las opciones de salida controlan donde y como se generan los archivos compilados:
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"declarationDir": "./types"
}
}
- outDir: carpeta de salida para los archivos
.js. - rootDir: carpeta raíz del código fuente. Determina la estructura de directorios dentro de
outDir. - sourceMap: genera archivos
.js.mapque permiten depurar el código TypeScript original en el navegador o en Node.js. - declaration: genera archivos
.d.tscon las definiciones de tipos. Necesario para publicar bibliotecas TypeScript. - declarationMap: genera
.d.ts.mapque permite navegar desde las definiciones de tipos al código fuente. - declarationDir: carpeta separada para los archivos
.d.ts.
Los source maps son esenciales durante el desarrollo. Permiten que el depurador muestre el código TypeScript original en lugar del JavaScript compilado. En producción, conviene separar los source maps o no incluirlos.
Los declaration files (.d.ts) son necesarios cuando se pública una biblioteca TypeScript. Permiten que otros proyectos utilicen el código con información de tipos completa sin acceder al código fuente.
Path aliases
Los path aliases simplifican las importaciones en proyectos con estructuras de carpetas profundas:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@utils/*": ["src/utils/*"],
"@modelos/*": ["src/modelos/*"],
"@servicios/*": ["src/servicios/*"]
}
}
}
Con esta configuración, las importaciones se simplifican:
// Sin path aliases: ruta relativa compleja
import { formatearFecha } from "../../../utils/fechas";
import { Usuario } from "../../../modelos/usuario";
// Con path aliases: ruta limpia
import { formatearFecha } from "@utils/fechas";
import { Usuario } from "@modelos/usuario";
Los path aliases de TypeScript son solo información para el compilador. Para que funcionen en tiempo de ejecución, se necesita configurar también el bundler o la herramienta de ejecución (Vite, webpack, tsx, etc.).
En proyectos que usan tsx para ejecución directa, se puede utilizar el paquete tsconfig-paths:
npm install --save-dev tsconfig-paths
npx ts-node -r tsconfig-paths/register src/index.ts
Herencia con extends
La opción extends permite heredar configuración de un archivo base. Esto es útil cuando se comparten opciones entre múltiples proyectos o entre distintas configuraciones del mismo proyecto (por ejemplo, una para el servidor y otra para los tests):
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"module": "NodeNext"
},
"include": ["src/**/*"]
}
El archivo base contiene las opciones comunes:
{
"compilerOptions": {
"target": "ES2022",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
Se puede heredar de múltiples archivos usando un array:
{
"extends": ["./tsconfig.base.json", "./tsconfig.paths.json"]
}
Las opciones del archivo hijo sobreescriben las del padre. Esto permite mantener una configuración DRY (Don't Repeat Yourself) en monorepos con múltiples paquetes.
lib: tipos de ambiente disponibles
La opción lib determina que declaraciones de tipos de ambiente están disponibles en el proyecto. Controla si el código tiene acceso a APIs del navegador, del servidor o de versiones específicas de JavaScript:
{
"compilerOptions": {
"lib": ["ES2022", "DOM", "DOM.Iterable"]
}
}
Valores comunes:
ES2022,ES2023: APIs del estándar JavaScript.DOM: API del navegador (window, document, fetch, etc.).DOM.Iterable: soporte para iteración de colecciones del DOM.WebWorker: API de Web Workers.
Para proyectos Node.js que no acceden al DOM, se omite DOM y se usa el paquete @types/node:
npm install --save-dev @types/node
{
"compilerOptions": {
"lib": ["ES2022"],
"types": ["node"]
}
}
Opciones de compatibilidad
Dos opciones que conviene activar en la mayoría de proyectos:
{
"compilerOptions": {
"skipLibCheck": true,
"esModuleInterop": true
}
}
skipLibCheck: true omite la comprobación de tipos en archivos .d.ts de node_modules. Acelera la compilación y evita errores en bibliotecas de terceros con definiciones de tipos defectuosas.
esModuleInterop: true permite importar módulos CommonJS con la sintaxis de ES modules:
// Sin esModuleInterop: requiere * as
import * as express from "express";
// Con esModuleInterop: importación limpia
import express from "express";
Configuración completa recomendada
Una configuración sólida para un proyecto Node.js moderno:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src",
"sourceMap": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Para un proyecto frontend con un bundler como Vite:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"noEmit": true
},
"include": ["src/**/*"]
}
En proyectos con bundler, la opción noEmit: true indica que TypeScript solo verifica tipos sin generar archivos .js, ya que esa tarea la realiza el bundler.
Type stripping y erasableSyntaxOnly
Node.js incorpora de forma nativa la capacidad de ejecutar archivos .ts eliminando la sintaxis de tipos sin necesidad de un paso de compilación previo. Esta funcionalidad, conocida como type stripping, permite ejecutar directamente node archivo.ts a partir de Node.js 22.6+.
Para que el type stripping funcione, el código TypeScript solo puede contener sintaxis borrable, es decir, anotaciones de tipo que se eliminan sin alterar el comportamiento del programa. La opción erasableSyntaxOnly garantiza que el código cumple esta restricción:
{
"compilerOptions": {
"erasableSyntaxOnly": true
}
}
Con esta opción activada, TypeScript emite un error si se utilizan construcciones que no son puramente borrables:
// Error con erasableSyntaxOnly: enums generan código en tiempo de ejecución
enum Estado {
Activo,
Inactivo
}
// Error: namespaces con código de ejecución
namespace MiApp {
export function iniciar() {}
}
// Error: parameter properties en constructores
class Usuario {
constructor(public nombre: string) {}
}
La opción
erasableSyntaxOnlyes especialmente relevante para proyectos que se ejecutan directamente con Node.js sin compilación previa. Si el proyecto usatsccomo paso de compilación, esta restricción no es necesaria.
Las alternativas compatibles con type stripping para los casos anteriores son:
// En lugar de enum, usar objetos constantes
const Estado = {
Activo: 0,
Inactivo: 1
} as const;
type Estado = typeof Estado[keyof typeof Estado];
// En lugar de parameter properties, asignar en el cuerpo
class Usuario {
nombre: string;
constructor(nombre: string) {
this.nombre = nombre;
}
}
Reescritura de extensiones en importaciones
La opción rewriteRelativeImportExtensions transforma automáticamente las extensiones .ts, .tsx, .mts y .cts en sus equivalentes JavaScript durante la compilación:
{
"compilerOptions": {
"rewriteRelativeImportExtensions": true
}
}
// Código fuente con extensión .ts
import { utilidad } from "./helpers.ts";
// JavaScript generado: extensión convertida a .js
import { utilidad } from "./helpers.js";
Esta opción simplifica el desarrollo en entornos donde el editor y los runtimes esperan extensiones .ts en las importaciones, pero el JavaScript resultante necesita extensiones .js.
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en TypeScript
Documentación oficial de TypeScript
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, TypeScript es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de TypeScript
Explora más contenido relacionado con TypeScript y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Comprender la estructura y propósito de tsconfig.json. Configurar correctamente target, module y moduleResolution para diferentes entornos. Activar y entender cada opción del modo estricto de TypeScript. Usar paths y baseUrl para eliminar rutas relativas complejas. Configurar la generación de source maps y declaration files para bibliotecas. Conocer las opciones modernas como erasableSyntaxOnly para type stripping nativo en Node.js.