En esta página

Configuración y tsconfig.json

12 min lectura TextoCap. 1 — Fundamentos de TypeScript

Configuración y tsconfig.json

Antes de escribir una sola línea de TypeScript en un proyecto real, necesitas configurar el compilador correctamente. Un tsconfig.json mal configurado puede dejarte con un TypeScript que no te protege de casi nada, o peor aún, con un proyecto que compila pero genera JavaScript incompatible con tu entorno de destino. Esta lección te da el conocimiento para configurar TypeScript correctamente desde el primer día.

Instalación

TypeScript se instala como dependencia de desarrollo. En la mayoría de los proyectos no necesitas TypeScript en producción, ya que el código se compila antes del despliegue.

# Con npm
npm install --save-dev typescript

# Con bun (más rápido)
bun add -d typescript

# Verificar la versión instalada
npx tsc --version
# Version 6.0.0

Para inicializar un tsconfig.json con valores por defecto razonables:

npx tsc --init

Esto genera un archivo con todas las opciones posibles, la mayoría comentadas. Te recomiendo empezar desde cero con solo las opciones que necesitas, que es lo que haremos en esta lección.

La estructura del tsconfig.json

El archivo tsconfig.json tiene tres secciones principales:

{
  "compilerOptions": { },
  "include": [],
  "exclude": []
}
  • compilerOptions: controla el comportamiento del compilador.
  • include: qué archivos TypeScript debe procesar.
  • exclude: qué archivos o directorios ignorar.

También existe extends para heredar configuración de otro archivo, y references para proyectos compuestos (monorepos).

compilerOptions: las opciones esenciales

`target`: el JavaScript de destino

"target": "ES2022"

Define a qué versión de JavaScript se compila el código. Si tu entorno es Node.js 18+, usa ES2022. Si necesitas soportar navegadores sin bundler, ajusta según el soporte requerido. Los valores comunes son ES5, ES6/ES2015, ES2020, ES2022, y ESNext.

Con target: "ES2022" TypeScript puede emitir clases nativas, async/await nativo, métodos de array modernos como .at(), y operadores de asignación lógica (??=, &&=, ||=).

`module`: el sistema de módulos

"module": "NodeNext"

Controla cómo se emiten las importaciones y exportaciones. Las opciones más relevantes en 2025 son:

  • NodeNext: Para Node.js con ESM o CJS. TypeScript lee el campo type de tu package.json para determinar el formato.
  • ESNext: Para bundlers modernos (Vite, webpack, esbuild) que manejan la resolución ellos mismos.
  • CommonJS: Para Node.js legacy.

`moduleResolution`: cómo se resuelven los imports

"moduleResolution": "NodeNext"

Debe estar sincronizado con module. Si usas module: "NodeNext", usa moduleResolution: "NodeNext". Para bundlers, "Bundler" es la opción correcta en TypeScript 5+.

Con NodeNext, TypeScript requiere extensiones explícitas en los imports relativos:

// ✅ Correcto con NodeNext
import { calcular } from './utils.js'; // Nota: .js aunque el archivo sea .ts

// ❌ Incorrecto con NodeNext
import { calcular } from './utils';

`strict`: el escudo completo

"strict": true

Esta es la opción más importante. Activa un conjunto de verificaciones que hacen TypeScript realmente útil:

  • strictNullChecks: null y undefined son tipos separados. Sin esto, puedes asignar null a cualquier variable sin que TypeScript se queje.
  • noImplicitAny: Obliga a declarar tipos cuando TypeScript no puede inferirlos. Sin esto, TypeScript acepta any implícito silenciosamente.
  • strictFunctionTypes: Verifica la covarianza/contravarianza en tipos de funciones.
  • useUnknownInCatchVariables: En bloques catch, la variable de error es unknown en lugar de any.

Opciones adicionales que deberías activar

"noUncheckedIndexedAccess": true

Al acceder a un arreglo por índice (arr[0]), el tipo resultante incluye undefined. Esto fuerza a verificar que el elemento existe antes de usarlo, eliminando un tipo muy común de bug en tiempo de ejecución.

const nombres = ['Ana', 'Carlos', 'María'];
const primero = nombres[0]; // tipo: string | undefined (con noUncheckedIndexedAccess)
console.log(primero.toUpperCase()); // ❌ Error: 'primero' is possibly 'undefined'
console.log(primero?.toUpperCase()); // ✅ 'ANA'
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true

Estas opciones detectan código muerto y rutas de retorno faltantes.

`outDir` y `rootDir`: organización de archivos

"outDir": "./dist",
"rootDir": "./src"

rootDir indica dónde están los archivos fuente TypeScript. outDir indica dónde se generan los archivos JavaScript compilados. Esta separación mantiene el repositorio limpio.

`esModuleInterop`: compatibilidad con módulos CommonJS

"esModuleInterop": true

Permite importar módulos CommonJS (como la mayoría de los paquetes npm) con sintaxis de import por defecto:

// Sin esModuleInterop:
import * as fs from 'fs';

// Con esModuleInterop:
import fs from 'fs';

`paths`: alias de importación

"paths": {
  "@utils/*": ["./src/utils/*"],
  "@models/*": ["./src/models/*"]
}

Los alias permiten importar con rutas cortas en lugar de rutas relativas largas:

// Sin alias: relativo y frágil
import { formatearFecha } from '../../../utils/fecha';

// Con alias: limpio y robusto
import { formatearFecha } from '@utils/fecha';

Importante: paths solo afecta a TypeScript. Si usas un bundler, también debes configurar los alias ahí (Vite: resolve.alias, webpack: resolve.alias).

`declaration` y `sourceMap`: para librerías

"declaration": true,
"declarationMap": true,
"sourceMap": true

declaration: true genera archivos .d.ts con las definiciones de tipos, necesario si publicas una librería npm. sourceMap: true genera mapas de fuente para depuración, conectando el JavaScript compilado con el TypeScript original.

Ejecutar el compilador

# Compilar una vez
npx tsc

# Compilar en modo watch (recompila al guardar)
npx tsc --watch

# Verificar tipos sin emitir archivos
npx tsc --noEmit

# Compilar un archivo específico (ignora tsconfig.json)
npx tsc archivo.ts --target ES2022

El modo --noEmit es especialmente útil en CI/CD para verificar que el código compila correctamente sin generar archivos.

TypeScript en Node.js: tsx vs ts-node

Para ejecutar archivos TypeScript directamente sin compilar, tienes varias opciones:

tsx (recomendado): usa esbuild internamente, es muy rápido y soporta import.meta.

npx tsx src/index.ts
# O instalar globalmente:
npm install -g tsx
tsx src/index.ts

ts-node: la opción clásica, más lenta pero con más opciones de configuración.

npm install -D ts-node
npx ts-node src/index.ts

Bun: ejecuta TypeScript nativamente, sin configuración adicional.

bun run src/index.ts

Para scripts de desarrollo, seeds de base de datos y herramientas internas, tsx es la elección pragmática: sin pasos de compilación, sin archivos generados.

Estructura recomendada para un proyecto Node.js

mi-proyecto/
├── src/
│   ├── index.ts
│   ├── models/
│   └── utils/
├── dist/           (generado por tsc, añadir a .gitignore)
├── tsconfig.json
├── package.json
└── .gitignore

El package.json típico:

{
  "name": "mi-proyecto",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "dev": "tsx watch src/index.ts",
    "typecheck": "tsc --noEmit",
    "start": "node dist/index.js"
  }
}

Con esta configuración tienes un flujo de desarrollo fluido: npm run dev para desarrollo con recarga automática, npm run typecheck para verificar tipos en CI, y npm run build para producción.

Herencia de configuración con `extends`

Para proyectos con múltiples entornos (producción, pruebas, desarrollo), puedes usar extends:

// tsconfig.base.json
{
  "compilerOptions": {
    "target": "ES2022",
    "strict": true,
    "esModuleInterop": true
  }
}

// tsconfig.json (hereda y sobreescribe)
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

La comunidad de TypeScript mantiene configuraciones base curadas en el paquete @tsconfig/bases:

npm install -D @tsconfig/node22
{
  "extends": "@tsconfig/node22/tsconfig.json",
  "compilerOptions": {
    "outDir": "./dist"
  }
}

Con la configuración correcta establecida, el compilador es tu aliado. En la siguiente lección comenzamos a explorar el sistema de tipos en sí: tipos primitivos, anotaciones y cuándo dejar que TypeScript infiera por ti.

strict: true no es opcional
Habilitar 'strict: true' activa 7 verificaciones críticas a la vez: strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitAny, noImplicitThis y useUnknownInCatchVariables. Sin 'strict', TypeScript pierde la mitad de su valor protector.
tsx para ejecutar TypeScript sin compilar
Para scripts y desarrollo, usa 'npx tsx archivo.ts' o instala 'tsx' globalmente. Ejecuta TypeScript directamente usando esbuild internamente, sin generar archivos .js. Perfecto para scripts, seeds de base de datos y herramientas de desarrollo.