La mayoría de los proyectos TypeScript que analizo tienen "strict": true en el tsconfig.json, pero también tienen // @ts-ignore esparcidos por el código, any en lugares estratégicos e ingenieros que no saben qué protege el strict mode.

Strict mode no es un conjunto arbitrario de restricciones para irritar al compilador. Son flags, cada una capturando una categoría de bug que ocurre en runtime. Entender qué hace cada flag cambia "el TypeScript me obligó a hacer esto" a "el TypeScript me impidió cometer ese error".

Qué habilita "strict": true

"strict": true es un atajo para habilitar un conjunto de flags. Vamos con lo que importa:

strictNullChecks

La más importante. Sin ella, null y undefined son asignables a cualquier tipo. Con ella, necesitas lidiar con la posibilidad de ausencia.

// Sin strictNullChecks: compila, puede explotar en runtime
function getUser(id: string): User {
  return db.find(id) // puede retornar undefined
}
const name = getUser('123').name // TypeError: Cannot read property 'name' of undefined

// Con strictNullChecks: error en tiempo de compilación
function getUser(id: string): User | undefined {
  return db.find(id)
}
const user = getUser('123')
const name = user?.name // forzado a lidiar con el undefined

noImplicitAny

Fuerza el tipado explícito cuando TypeScript no puede inferir el tipo. Sin esto, los parámetros de función sin tipo se vuelven any sin aviso, y pierdes toda la protección del sistema de tipos.

// ❌ Sin noImplicitAny: param es `any` implícitamente
function process(data) { // param: any
  return data.value.toUpperCase() // sin verificación
}

// ✅ Con noImplicitAny: declaras la intención
function process(data: { value: string }) {
  return data.value.toUpperCase()
}

strictFunctionTypes

Garantiza verificación de tipos correcta en callbacks. Sin ella, las funciones se verifican bivariantly, lo que puede dejar pasar bugs de tipo.

// strictFunctionTypes captura este bug
type Handler = (event: MouseEvent) => void
const myHandler: Handler = (event: Event) => {} // ❌ Error correcto: MouseEvent es más específico

strictPropertyInitialization

Garantiza que las propiedades de clase se inicialicen en el constructor. Previene lecturas de propiedades undefined que parecen definidas.

class UserService {
  private db: Database // ❌ Error: no inicializado en el constructor

  constructor() {
    // olvidaste inicializar db
  }
}

// ✅ Correcto
class UserService {
  private db: Database

  constructor(db: Database) {
    this.db = db
  }
}

Flags que no están en strict pero deberías habilitar

noUncheckedIndexedAccess

Los accesos a arrays y objetos con índice retornan T | undefined en lugar de T. Previene uno de los bugs más comunes: asumir que un índice siempre existe.

// tsconfig.json
{ "compilerOptions": { "noUncheckedIndexedAccess": true } }

const items = ['a', 'b', 'c']
const first = items[0] // tipo: string | undefined (no string)
const safe = first?.toUpperCase() // forzado a verificar

exactOptionalPropertyTypes

Diferencia { prop?: string } (prop puede estar ausente) de { prop: string | undefined } (prop presente pero undefined). Son cosas diferentes, y sin esa flag, TypeScript las trata igual.

Cómo migrar un proyecto legado a strict

Habilitar strict de una sola vez en un proyecto grande no es práctico. La estrategia que funciona:

  1. Habilita "strict": true y usa // @ts-ignore en todo lo que se rompa (sí, temporalmente)
  2. Crea una regla de lint que prohíba nuevos // @ts-ignore, el legado puede existir, lo nuevo no
  3. Gradualmente, resuelve los @ts-ignore archivo por archivo, priorizando los más críticos

Esto da el beneficio inmediato de strict en el código nuevo mientras el legado se corrige poco a poco.

Por qué no deshabilitar lo que no entiendes

Cada vez que agregas // @ts-ignore, as any, o deshabilitas una flag, estás diciendo: "Confío en mí mismo más que en el compilador aquí." A veces eso es legítimo, por ejemplo al trabajar con librerías mal tipadas.

La mayoría de las veces, es escape. Y el TypeScript te está protegiendo de algo que va a explotar en runtime. Quizás no mañana. Quizás en 6 meses. Quizás en producción un viernes a las 23h.

Cuando no entiendes por qué el TypeScript se está quejando, la respuesta correcta es entender, no silenciar. Usar TypeScript y usar TypeScript bien son cosas diferentes.