tsconfig.json y opciones del compilador

Intermedio
TypeScript
TypeScript
Actualizado: 18/04/2026

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.

Mapa de opciones del compilador tsconfig.json

{
  "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 .ts del 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.map que permiten depurar el código TypeScript original en el navegador o en Node.js.
  • declaration: genera archivos .d.ts con las definiciones de tipos. Necesario para publicar bibliotecas TypeScript.
  • declarationMap: genera .d.ts.map que 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 erasableSyntaxOnly es especialmente relevante para proyectos que se ejecutan directamente con Node.js sin compilación previa. Si el proyecto usa tsc como 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 - Autor del tutorial

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.