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

推荐订阅源

Google Online Security Blog
Google Online Security Blog
D
Docker
人人都是产品经理
人人都是产品经理
Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
L
LangChain Blog
月光博客
月光博客
U
Unit 42
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
Recent Announcements
Recent Announcements
MyScale Blog
MyScale Blog
N
Netflix TechBlog - Medium
D
DataBreaches.Net
T
Tailwind CSS Blog
H
Help Net Security
MongoDB | Blog
MongoDB | Blog
V
Visual Studio Blog
B
Blog
G
Google Developers Blog
有赞技术团队
有赞技术团队
Y
Y Combinator Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
云风的 BLOG
云风的 BLOG
Recorded Future
Recorded Future
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Google DeepMind News
Google DeepMind News
Jina AI
Jina AI
Engineering at Meta
Engineering at Meta
C
Check Point Blog
V
V2EX
爱范儿
爱范儿
Microsoft Azure Blog
Microsoft Azure Blog
T
The Blog of Author Tim Ferriss
博客园 - 聂微东
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
F
Full Disclosure
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
P
Proofpoint News Feed
罗磊的独立博客
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Google DeepMind News
Google DeepMind News
WordPress大学
WordPress大学
Apple Machine Learning Research
Apple Machine Learning Research
量子位
博客园 - 司徒正美
博客园 - 叶小钗

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 Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
TypeScript strict mode: las 6 opciones del tsconfig que más impactan en producción y cuándo activarlas
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