Activar strict: true en el tsconfig es el primer paso que se recomienda en cualquier guía de TypeScript, pero pocas explican qué activa exactamente o qué queda fuera. strict es un alias para siete opciones concretas, y existen al menos cinco opciones adicionales de rigor que no forman parte del grupo pero que conviene conocer para decidir si activarlas. Esta guía las explica todas con ejemplos de los errores que detectan.
Las siete opciones de strict
// Equivalente exacto de "strict": true
{
"compilerOptions": {
"strictNullChecks": true, // null y undefined no son asignables a otros tipos
"strictFunctionTypes": true, // contravarianza en parámetros de función
"strictBindCallApply": true, // tipos correctos para bind/call/apply
"strictPropertyInitialization": true, // las propiedades de clase deben inicializarse
"noImplicitAny": true, // error cuando TypeScript inferiría any
"noImplicitThis": true, // error cuando this tiene tipo any implícito
"alwaysStrict": true // "use strict" en cada archivo emitido
}
}
strictNullChecks: el más importante
// Sin strictNullChecks (permisivo):
let nombre: string = null; // OK (mal)
// Con strictNullChecks:
let nombre: string = null; // Error: null no es asignable a string
let nombreONulo: string | null = null; // OK (correcto)
function buscarUsuario(id: number): Usuario | undefined {
return id === 1 ? { id: 1, email: "[email protected]" } : undefined;
}
const usuario = buscarUsuario(2);
// Sin strictNullChecks: usuario.email no da error (crash en runtime)
// Con strictNullChecks: Error: usuario es posiblemente undefined
console.log(usuario?.email); // Correcto con optional chaining
noImplicitAny: forzar tipos explícitos
// Error con noImplicitAny:
function procesar(datos) { // Error: 'datos' implicitly has an 'any' type
return datos.length;
}
// Correcto:
function procesar(datos: string[]): number {
return datos.length;
}
// También afecta a los callbacks:
const numeros = [1, 2, 3];
numeros.forEach(n => console.log(n)); // n: number (inferido)
// [1,2,3].map(x => x * 2) // x: number (inferido, no any)
strictPropertyInitialization
class Servicio {
// Error: 'cliente' no está inicializado en el constructor
cliente: HttpClient;
// Correcto: inicializar en el constructor
constructor(private http: HttpClient) {
this.cliente = http;
}
}
// Si el valor viene de otro lugar (p.ej. un decorador @Inject),
// usa el operador de aserción de asignación definitiva:
class OtroServicio {
cliente!: HttpClient; // ! = "lo inicializaré yo antes de usarlo"
}
Opciones fuera de strict
// noUncheckedIndexedAccess (muy recomendable):
// Acceder a un array/objeto por índice devuelve T | undefined
const lista = ["a", "b", "c"];
const primero = lista[0]; // string | undefined (no solo string)
const segundo = lista[99]; // string | undefined (correcto, puede ser undefined)
if (primero !== undefined) {
console.log(primero.toUpperCase()); // OK
}
// exactOptionalPropertyTypes:
interface Config {
timeout?: number;
}
const config: Config = {};
// Sin exactOptional: config.timeout = undefined es OK
// Con exactOptional: config.timeout = undefined es error
// (solo puedes no asignar la propiedad, no asignarle undefined explícitamente)
noPropertyAccessFromIndexSignature
// noPropertyAccessFromIndexSignature:
interface Diccionario {
[clave: string]: string;
titulo: string; // propiedad conocida
}
const d: Diccionario = { titulo: "Hola", otro: "mundo" };
d.titulo; // OK siempre
d["titulo"]; // OK siempre
d.noExiste; // Con esta opción: Error (usa d["noExiste"])
d["noExiste"]; // OK (acceso explícito por índice)
Estrategia de migración gradual
// Para activar strict en un proyecto existente sin romper todo a la vez:
{
"compilerOptions": {
// Fase 1: activar las menos disruptivas
"noImplicitAny": true,
"alwaysStrict": true,
// Fase 2: una vez corregidos los any implícitos
"strictNullChecks": true,
// Fase 3: cuando ya hay tipos en todo el proyecto
"strict": true,
// Fase 4 (opcional, más rigor):
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
El valor de activar todas estas opciones no está en que el código compile más lento ni en que haya más líneas con tipos: está en que los errores que habrían ocurrido en producción accesos a undefined, confusión de tipos en callbacks, propiedades no inicializadas se detectan en el IDE, antes de ejecutar nada. Cada opción de rigor que activas es una clase de bug que dejas de poder escribir.
Imagen: Pexels / Bibek Ghosh
