En la arquitectura de las aplicaciones web modernas, el archivo tsconfig.json actúa como el manifiesto raíz del analizador estático y compilador. Una configuración incorrecta del compilador (tsc) puede devaluar por completo el sistema de tipos: permitir accesos en tiempo de ejecución a propiedades inexistentes de objetos, emitir código ESM roto durante la transpilación y ocultar incompatibilidades críticas en paquetes duales (CJS/ESM).
Históricamente, la opción "strict": true se interpretaba como un flag exhaustivo de máxima rigurosidad. Sin embargo, en aplicaciones enterprise, strict solo habilita un subconjunto básico de validaciones. Flags críticos de seguridad de tipos —como noUncheckedIndexedAccess y exactOptionalPropertyTypes— quedan deliberadamente fuera del paraguas strict por razones de retrocompatibilidad con bases de código heredadas.
Con la transición de la industria hacia empaquetadores modernos (Vite, Turbopack, Rspack, Bun) y el soporte nativo de ESM en Node.js, el paradigma de resolución de módulos ha cambiado radicalmente: las estrategias obsoletas (moduleResolution: "node", "node10") han sido reemplazadas por algoritmos especializados: moduleResolution: "bundler" y moduleResolution: "nodenext".
La interacción de los flags del compilador TypeScript se puede estructurar en tres perímetros operativos independientes: el perímetro de comprobación de tipos (Type Checking), el de resolución de módulos (Module Resolution) y el de transpilación/emisión (Emit & Transpilation).
+-----------------------------------------------------------------------+
| PERÍMETROS ARQUITECTÓNICOS DE TSCONFIG |
+-----------------------------------------------------------------------+
| 1. PERÍMETRO DE TYPE-SAFETY (Type Checking) |
| "strict": true |
| +-- strictNullChecks, noImplicitAny, strictFunctionTypes |
| Enterprise Hardening (Más allá de strict): |
| +-- noUncheckedIndexedAccess (arrays y mapas seguros) |
| +-- exactOptionalPropertyTypes (distinguir ausencia de undef) |
+-----------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------+
| 2. PERÍMETRO DE RESOLUCIÓN DE MÓDULOS (Module Resolution) |
| Runtime Node.js: Bundler / Herramientas Modernas: |
| "module": "nodenext" "module": "esnext" |
| "moduleResolution": "moduleResolution": "bundler" |
| "nodenext" (ESM estricto) (Vite / Next.js App Router / Rspack) |
+-----------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------+
| 3. PERÍMETRO DE TRANSPILACIÓN Y EMISIÓN (Transpilation & Emit) |
| "isolatedModules": true (garantía para esbuild/SWC) |
| "verbatimModuleSyntax": true (import/export estrictos type-only) |
| "skipLibCheck": true (omite validación de .d.ts en node_mods)|
+-----------------------------------------------------------------------+TypeError: Cannot read properties of undefined: Activar noUncheckedIndexedAccess elimina fallos silenciosos al consultar claves dinámicas en payloads JSON de APIs.@repo/tsconfig/base.json, @repo/tsconfig/nextjs.json, @repo/tsconfig/library.json) para evitar la duplicación y discrepancias.isolatedModules: true y verbatimModuleSyntax: true garantiza que compiladores de archivo único no fallen en runtime por construcciones que requieren contexto global (const enum o reexportaciones de tipos sin marcar).skipLibCheck reduce los tiempos de tsc --noEmit entre 3x y 5x sin comprometer la seguridad de tipos de la lógica de dominio.El flag paraguas "strict": true activa 8 comprobaciones fundamentales:
noImplicitAny: Prohíbe la inferencia implícita de tipo any.strictNullChecks: Segrega null/undefined de los tipos de valor concretos.strictFunctionTypes: Valida contravariancia en parámetros de funciones.strictBindCallApply: Tipado estricto en llamadas a bind, call y apply.strictPropertyInitialization: Exige inicializar propiedades de clase en constructores.noImplicitThis: Prohíbe contextos this implícitos no tipados.alwaysStrict: Emite la directiva "use strict" en todos los archivos compilados.useUnknownInCatchVariables: Tipa la variable de catch (err) como unknown en vez de any.Para alcanzar una robustez verdaderamente enterprise, se requieren flags de endurecimiento adicionales:
// Demostración del efecto de noUncheckedIndexedAccess
interface MetricRegistry {
[metricName: string]: number;
}
function computeThroughput(registry: MetricRegistry, key: string): number {
const value = registry[key];
// ❌ Sin noUncheckedIndexedAccess: el tipo de value se infiere como number.
// En runtime, si la clave no existe devuelve undefined, produciendo NaN:
// return value * 2;
// ✅ Con noUncheckedIndexedAccess: el tipo de value se infiere como number | undefined.
// El compilador exige validación defensiva explícita:
if (typeof value === 'undefined') {
throw new Error(`La métrica "${key}" no existe en el registro`);
}
return value * 2;
}El flag exactOptionalPropertyTypes resuelve la colisión fundamental entre la ausencia de una clave y una clave explícitamente establecida en undefined:
// Demostración de exactOptionalPropertyTypes
interface CacheConfig {
ttlMs?: number; // Con el flag: la clave puede omitirse o ser number, pero NO undefined
}
// ❌ Error de compilación con exactOptionalPropertyTypes: true:
// Type 'undefined' is not assignable to type 'number' with 'exactOptionalPropertyTypes: true'.
const invalidConfig: CacheConfig = {
ttlMs: undefined,
};
// ✅ Configuraciones válidas:
const validConfig1: CacheConfig = {};
const validConfig2: CacheConfig = { ttlMs: 5000 };Introducido en TypeScript 5.0+, el flag verbatimModuleSyntax establece reglas uniformes y deterministas de elisión de tipos.
Antes de este flag, el compilador intentaba inferir si un identificador importado era un valor o un tipo. Cuando solo se referenciaba en posiciones de tipo, tsc eliminaba en silencio todo el import, rompiendo importaciones con efectos secundarios (side-effects).
// Con verbatimModuleSyntax: true
// ✅ Importación explícita de tipos: garantizado que se elimina del JS compilado
import type { RequestPayload, UserSession } from '@/entities/session';
// ✅ Importación estándar: garantizado que se conserva como ESM import nativo
import { initializeTracing } from '@/infrastructure/tracer';
// ❌ Error de compilación si TokenValidator es una interfaz o tipo en lugar de clase/objeto:
// 'TokenValidator' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.
import { TokenValidator } from '@/infrastructure/auth';Lectura de config raíz, resolución de la cadena extends y descubrimiento de archivos por globs include/exclude.
Mapeo de importaciones vía algoritmo bundler o nodenext analizando campos exports en node_modules.
Evaluación de nodos AST, validación de reglas strict y verificación de noUncheckedIndexedAccess.
Generación de código JavaScript (.js), archivos de declaración (.d.ts) y mapas de fuentes (.d.ts.map).
A continuación se presenta una configuración de producción para aplicaciones web modernas (Next.js 15+, React 19, Vite, microservicios Node.js) con un nivel intransigente de rigurosidad:
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Enterprise Strict Modern Base",
"compilerOptions": {
/* Parámetros Base de Entorno y Sintaxis */
"target": "ES2022",
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "bundler",
"moduleDetection": "force",
/* Suite Completa de Verificación Estricta */
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"useUnknownInCatchVariables": true,
/* Endurecimiento Más Allá de Strict */
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
/* Arquitectura de Módulos y Compatibilidad con Bundlers */
"isolatedModules": true,
"verbatimModuleSyntax": true,
"resolveJsonModule": true,
"allowJs": false,
"checkJs": false,
/* Emisión y Mapas de Código */
"noEmit": true,
"declaration": false,
"declarationMap": false,
"sourceMap": true,
/* Optimización de Rendimiento del Compilador */
"skipLibCheck": true,
"incremental": true
}
}Los ingenieros a menudo asumen que "skipLibCheck": false garantiza una seguridad total sobre el código de terceros. En realidad, el compilador comienza a validar recursivamente la coherencia interna de todos los archivos .d.ts en node_modules. Con docenas de dependencias, surgen inevitablemente conflictos por tipos globales duplicados (por ejemplo, versiones dispares de @types/node), rompiendo la compilación sin aportar ningún beneficio al código de negocio.
// ❌ Antipatrón: ralentiza la compilación 4x-10x y rompe el build con tipos externos
{
"compilerOptions": {
"skipLibCheck": false
}
}
// ✅ Práctica recomendada: validar únicamente el código fuente propio
{
"compilerOptions": {
"skipLibCheck": true
}
}Configurar "node" obliga a TypeScript a buscar paquetes bajo las reglas de Node.js de hace una década, ignorando el campo "exports" de los package.json de librerías modernas. Como resultado, TypeScript aprueba importaciones que fallan durante el bundling o en runtime.
// ❌ Configuración obsoleta: no soporta conditional exports modernos
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "node"
}
}
// ✅ Estándar moderno para empaquetadores (Next.js, Vite, Rspack)
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler"
}
}Vite y Next.js transpilan cada archivo de forma aislada mediante esbuild o SWC sin construir el árbol global de tipos. Sin isolatedModules: true, TypeScript permitirá construcciones que requieren análisis de proyecto completo (como const enum o reexportación de tipos sin export type), provocando errores en runtime durante el empaquetado.
Forzar importaciones exclusivas de tipos (import type), eliminando fugas cíclicas y errores de empaquetado aislado.
Reemplazar valores obsoletos node/classic por bundler para aplicaciones web o nodenext para librerías y Node.js.
Asegurar que strictNullChecks y noImplicitAny estén activos, remediando discrepancias directas en la lógica.
Imponer acceso seguro por índices. Proteger arrays y mapas con type guards o utilidades assert.
Separar propiedades omitidas de propiedades con valor undefined en contratos DTO y esquemas de formularios.