惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
博客园 - 【当耐特】
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
WordPress大学
WordPress大学
D
Docker
T
The Blog of Author Tim Ferriss
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Azure Blog
Microsoft Azure Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
月光博客
月光博客
M
MIT News - Artificial intelligence
H
Hackread – Cybersecurity News, Data Breaches, AI and More
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
云风的 BLOG
云风的 BLOG
F
Fortinet All Blogs
罗磊的独立博客
小众软件
小众软件
A
About on SuperTechFans
MyScale Blog
MyScale Blog
D
DataBreaches.Net
The GitHub Blog
The GitHub Blog
C
Check Point Blog
L
LangChain Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
TypeScript strict mode: las 6 opciones del tsconfig que m...
Juan Torchia · 2026-05-31 · via DEV Community

TypeScript strict mode: las 6 opciones del tsconfig que más impactan en producción y cuándo activarlas

Hay una escena que se repite. Alguien configura un proyecto nuevo, le dice a todo el mundo "usamos TypeScript estricto" y agrega strict: true al tsconfig.json. Todos asienten. El CI compila. Y tres meses después aparece un bug en producción que TypeScript podría haber atrapado si hubieran activado noUncheckedIndexedAccess.

Mi tesis es directa: strict: true es un atajo cómodo que activa seis flags razonables pero deja afuera dos opciones que, en mi criterio, previenen más bugs silenciosos que la mitad del grupo base. El problema no es strict: true en sí — es que la mayoría lo activa y siente que ya terminó.

Este post no es "activá strict y listo". Es un análisis bandera por bandera: qué hace cada una, qué tipo de error previene y cuál es el orden sensato para migrar una codebase que todavía no las tiene todas activadas.


Qué incluye strict: true — y qué no

Según la documentación oficial de TypeScript, strict: true es un shorthand que activa este conjunto de flags:

  • strictNullChecks
  • strictFunctionTypes
  • strictBindCallApply
  • strictPropertyInitialization
  • noImplicitAny
  • noImplicitThis
  • useUnknownInCatchVariables (desde TypeScript 4.4)
  • alwaysStrict (emite "use strict" en el output JS)

Lo que no activa por defecto:

  • noUncheckedIndexedAccess
  • exactOptionalPropertyTypes
  • noImplicitOverride
  • noPropertyAccessFromIndexSignature

Ese segundo grupo no vive bajo el paraguas de strict. Son flags independientes que TypeScript eligió no incluir porque generan muchos errores nuevos en codebases existentes. Eso no significa que sean opcionales para producción — significa que los diseñadores tomaron una decisión conservadora. Vos podés elegir diferente.


Las 6 opciones con mayor impacto real

1. strictNullChecks — la más importante del grupo base

Sin esto, null y undefined son asignables a cualquier tipo. Con esto activado:

// Sin strictNullChecks: compila sin error
function getUsername(user: User): string {
  return user.name; // user podría ser null
}

// Con strictNullChecks: el compilador exige que manejés el caso
function getUsername(user: User | null): string {
  if (!user) throw new Error("Usuario no encontrado");
  return user.name;
}

Enter fullscreen mode Exit fullscreen mode

Si tenés que elegir un único flag para activar hoy, es este. La mayoría de los crashes en runtime de aplicaciones TypeScript que no lo tienen activado tienen una firma común: Cannot read properties of undefined.

No hay discusión acá. Si no tenés strictNullChecks, no tenés TypeScript — tenés JavaScript con tipado cosmético.

2. noImplicitAny — el segundo prioritario

Cuando TypeScript no puede inferir el tipo de algo y vos no lo declaraste, tiene dos opciones: error o any silencioso. Sin este flag, elige any silencioso.

// Sin noImplicitAny: compila. 'data' es any implícito.
function process(data) {
  return data.toUpperCase(); // sin chequeo
}

// Con noImplicitAny: error. Tenés que declarar el tipo.
function process(data: string): string {
  return data.toUpperCase();
}

Enter fullscreen mode Exit fullscreen mode

El any implícito es como un agujero en el sistema de tipos. No lo ves, no te avisa, y se propaga. noImplicitAny lo cierra.

3. strictFunctionTypes — para quienes trabajan con callbacks y genéricos

Este flag hace que TypeScript verifique los tipos de los parámetros de funciones de forma contravariante (en lugar de bivariante). Es el más técnico del grupo y el que menos gente entiende, pero importa cuando pasás callbacks entre capas de la aplicación.

type Handler = (event: MouseEvent) => void;

// Sin strictFunctionTypes: esto compila aunque es inseguro
const handler: Handler = (event: Event) => {
  console.log((event as MouseEvent).clientX); // cast manual, riesgo
};

// Con strictFunctionTypes: error. MouseEvent no es assignable a Event en posición de parámetro.

Enter fullscreen mode Exit fullscreen mode

En una codebase de React con muchos event handlers, este flag atrapa asignaciones de función que parecen razonables pero esconden pérdidas de tipo en runtime.

4. useUnknownInCatchVariables — el underrated del grupo base

Antes de TypeScript 4.4, el error en un bloque catch era any. Con este flag activado, es unknown, lo que te fuerza a verificar su tipo antes de usarlo.

try {
  await fetchData();
} catch (error) {
  // Sin useUnknownInCatchVariables: error es 'any'
  // Con useUnknownInCatchVariables: error es 'unknown'

  if (error instanceof Error) {
    // Ahora sí podés acceder a error.message con seguridad
    console.error(error.message);
  } else {
    console.error("Error desconocido", error);
  }
}

Enter fullscreen mode Exit fullscreen mode

En sistemas donde el manejo de errores importa — autenticación, integraciones externas, procesamiento de pagos — este flag previene que asumas la forma del error sin validarla. Lo activa strict: true desde TS 4.4, pero vale la pena entender por qué existe.

5. noUncheckedIndexedAccess — el que más bugs previene fuera del grupo base

Este es el que no activa strict: true y el que más debería importarte. Cuando accedés a un array por índice o a un objeto por clave string, TypeScript por defecto asume que el valor existe. Con noUncheckedIndexedAccess, el tipo retornado incluye | undefined.

// tsconfig: noUncheckedIndexedAccess: true

const items = ["primero", "segundo", "tercero"];

const item = items[5]; 
// Sin noUncheckedIndexedAccess: item es 'string'
// Con noUncheckedIndexedAccess: item es 'string | undefined'

// Ahora el compilador te fuerza a chequearlo antes de usarlo:
if (item !== undefined) {
  console.log(item.toUpperCase()); // ✅
}

// Sin el chequeo: error de compilación
// console.log(item.toUpperCase()); // ❌ Object is possibly 'undefined'

Enter fullscreen mode Exit fullscreen mode

El mismo comportamiento aplica para index signatures:

const map: Record<string, number> = { a: 1 };

const value = map["b"];
// Sin noUncheckedIndexedAccess: value es 'number'
// Con noUncheckedIndexedAccess: value es 'number | undefined'

Enter fullscreen mode Exit fullscreen mode

La documentación oficial es clara en esto. ¿Por qué no está en strict? Porque genera muchos errores en codebases existentes donde el acceso por índice es ubicuo y nadie lo valida. Pero eso no lo hace opcional si querés cobertura real.

En escenarios con Prisma y resultados de queries, con respuestas de APIs externas casteadas a arrays, o con configuraciones leídas de JSON — este flag atrapa exactamente la clase de bug que aparece tarde, en producción, cuando el array llega vacío por primera vez.

6. exactOptionalPropertyTypes — el más subvalorado de todos

Este es el segundo que más gente ignora y el que más sutilmente rompe cosas. Sin este flag, TypeScript trata undefined como un valor válido para una propiedad opcional. Con él, hay una diferencia entre "la propiedad puede no estar" y "la propiedad está y vale undefined".

interface Config {
  timeout?: number; // propiedad opcional
}

// Sin exactOptionalPropertyTypes:
// Estas dos asignaciones son equivalentes para TypeScript:
const a: Config = {};                    // timeout no existe
const b: Config = { timeout: undefined }; // timeout existe pero es undefined

// Con exactOptionalPropertyTypes:
const c: Config = { timeout: undefined }; // ❌ Error
// Type 'undefined' is not assignable to type 'number'
// porque 'timeout?' significa 'puede no estar', no 'puede ser undefined'

Enter fullscreen mode Exit fullscreen mode

¿Por qué importa? Porque hay una diferencia operacional entre una clave ausente y una clave con valor undefined. En serialización JSON, en spreads de objetos, en Prisma updates — el comportamiento difiere. exactOptionalPropertyTypes hace que TypeScript entienda esa diferencia.


El orden para migrar una codebase existente

Si estás agregando esto a un proyecto que ya tiene código, el orden sensato es:

Paso 1: strictNullChecks     → más errores, más impacto, pero son los más urgentes
Paso 2: noImplicitAny        → segundo lote de errores, más fáciles de resolver
Paso 3: strict: true         → activa el resto del grupo base de golpe
Paso 4: noUncheckedIndexedAccess  → errores nuevos, pero son exactamente los que quería ver
Paso 5: exactOptionalPropertyTypes → último, requiere entender bien el modelo de datos

Enter fullscreen mode Exit fullscreen mode

Una estrategia útil para proyectos grandes es activar los flags con // @ts-expect-error de forma temporal y resolverlos archivo por archivo. Otra es usar skipLibCheck: true mientras migrás para no quedar bloqueado por tipos de dependencias que todavía no fueron actualizadas.

// tsconfig.json — configuración de migración progresiva
{
  "compilerOptions": {
    // Paso 1: empezá por acá
    "strictNullChecks": true,

    // Paso 2: una vez que el proyecto compila con el anterior
    "noImplicitAny": true,

    // Paso 3: activa el grupo base completo
    "strict": true,

    // Paso 4 y 5: después de estabilizar el grupo base
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,

    // Temporal durante la migración:
    "skipLibCheck": true
  }
}

Enter fullscreen mode Exit fullscreen mode


Los errores que más se cometen al migrar

Activar todo de una y abandonar. El CI explota con 400 errores y alguien decide que "TypeScript strict es demasiado restrictivo". El problema no es el flag — es el orden.

Usar as para silenciar en lugar de corregir. Cada as unknown as TipoQueQuiero es una deuda de tipos. Patea el error a runtime y hace que la migración sea cosmética.

// Esto no es una migración, es un disfraz:
const result = fetchUser() as User; // ❌ Ignora que fetchUser puede retornar null

// Esto sí:
const raw = await fetchUser();
if (!raw) throw new Error("Usuario no encontrado");
const result: User = raw; // ✅

Enter fullscreen mode Exit fullscreen mode

Ignorar los dos flags fuera de strict. Este es el error más común y el que motivó este post. Muchos equipos declaran TypeScript estricto sin saber que noUncheckedIndexedAccess no está incluido en ese preset.

Activar exactOptionalPropertyTypes sin revisar los updates de Prisma. En Prisma, los updates usan propiedades opcionales extensamente. Con este flag, hay patrones que antes compilaban y dejan de hacerlo. No es un bloqueo — es una señal de que había un modelo de datos impreciso. Pero conviene saber que el lote de errores va a aparecer ahí.


Qué no podés concluir solo con esto

Este análisis se basa en la documentación oficial y en patrones conocidos de TypeScript. Lo que no podés inferir de acá:

  • Cuántos errores va a generar en tu codebase específica. Eso solo lo sabés corriendo tsc --noEmit con cada flag activado.
  • Si exactOptionalPropertyTypes vale el costo en un proyecto con Prisma v5 sin refactors previos. Puede ser mucho trabajo por valor marginal si el modelo de datos está bien tipado de otra forma.
  • Si hay incompatibilidades con librerías de terceros que no soportan bien noUncheckedIndexedAccess. skipLibCheck: true mitiga esto, pero no lo elimina.

La decisión de cuándo activar cada flag requiere correr el compilador en el propio código y leer los errores. No hay atajos acá.


FAQ

¿strict: true activa noUncheckedIndexedAccess?
No. strict: true es un preset que activa ocho flags específicos documentados en la referencia oficial. noUncheckedIndexedAccess no es uno de ellos. Hay que activarlo por separado en el tsconfig.json.

¿Cuál es el primer flag que debería activar si mi proyecto no tiene ninguno?
strictNullChecks. Es el que previene la mayor clase de errores en runtime y es el prerequisito lógico para que el resto de los flags tenga sentido. Sin chequeos de null, los otros flags son decoración.

¿noImplicitAny rompe el uso de any explícito?
No. noImplicitAny solo penaliza el any que TypeScript infiere cuando no puede determinar el tipo. Si escribís any explícito (const x: any = ...), compila igual. Eso es intencional: a veces necesitás escaparte del sistema de tipos. Pero al menos lo hacés conscientemente.

¿Puedo activar estos flags progresivamente en un monorepo?
Sí. Cada paquete del monorepo puede tener su propio tsconfig.json que extiende una base compartida. Una estrategia común es activar los flags más estrictos en los paquetes nuevos y migrar los viejos de forma incremental. El riesgo es que los tipos que cruzan paquetes pueden quedar en zonas grises durante la transición.

¿exactOptionalPropertyTypes rompe los spreads de objetos?
Puede hacerlo si estás usando spreads para pasar propiedades opcionales con valor undefined. El compilador va a marcar esos casos porque hay una diferencia semántica entre propiedad ausente y propiedad con valor undefined. En la mayoría de los casos, el fix es usar narrowing o spreads condicionales en lugar de asumir que undefined pasa transparentemente.

¿Vale la pena activar todo esto en un proyecto que ya funciona?
Depende del costo de los bugs que querés prevenir. Si el sistema maneja autenticación, datos financieros o cualquier tipo de información donde un error silencioso tiene consecuencias reales — sí, vale la pena el costo de la migración. Si es un prototipo interno que no llega a usuarios — quizás strict: true alcanza por ahora. El criterio es el costo del error, no la comodidad del setup. Este tema conecta directamente con decisiones de arquitectura más amplias, del tipo de las que aparecen en el post sobre arquitectura backend de identidad digital: los flags no son decoración, son parte del contrato de seguridad del sistema.


Mi postura y el próximo paso concreto

strict: true es el piso, no el techo. El preset existe para que la adopción sea fácil, no para que la conversación termine ahí.

Los dos flags que más impacto tienen fuera del grupo base son noUncheckedIndexedAccess y exactOptionalPropertyTypes. El primero cierra la puerta a la clase de error más común en acceso a arrays y maps. El segundo hace que el modelo de tipos refleje la diferencia real entre "propiedad ausente" y "propiedad con valor undefined" — una distinción que importa en serialización, en Prisma y en cualquier código que recibe datos del exterior.

Lo que no compro es la actitud de "activé strict, ya está". Es la misma energía que agrega un healthcheck que solo verifica que el proceso responde — da una sensación de seguridad que no mide lo que creés que mide.

El próximo paso concreto: corré tsc --noEmit con noUncheckedIndexedAccess: true en el proyecto donde estás trabajando ahora. Leé los errores. Si son manejables, activalo. Si son 200+ errores, empezá por los archivos más críticos. No necesitás resolver todo de una — necesitás saber qué ignorabas.


Fuentes originales:


Este artículo fue publicado originalmente en juanchi.dev