Dominas los mecanismos de reducción de tipos (narrowing) y escribes tus propios predicados para hacer las uniones más fiables.
Abrir esta lección en KodokonEl narrowing (reducción de tipos) es el mecanismo por el cual TypeScript refina un tipo amplio (una unión, unknown) hacia otro más preciso analizando el flujo de control. Ya lo usas sin pensarlo: cada if sobre un valor tipado dispara un análisis. El compilador sigue tres familias de guardas integradas: typeof para los primitivos, instanceof para las instancias de clase e in para la presencia de una propiedad. Elegirlas bien te ahorra los cast imprudentes con as, que desactivan la verificación en lugar de guiarla.
function formatValue(
value: string | number | Date
): string {
if (typeof value === "string") {
return value.trim();
}
if (value instanceof Date) {
return value.toISOString();
}
return value.toFixed(2);
}El operador in es muy valioso cuando manejas objetos estructuralmente diferentes que no tienen ningún campo discriminante. Ten cuidado, sin embargo: in comprueba la presencia de una clave, incluidas las heredadas, no su tipo. Sobre datos externos (API, JSON), reduce según tus declaraciones, no según lo que realmente existe en tiempo de ejecución. La trampa clásica del mundo real: una comprobación typeof value === "object" deja pasar null, porque typeof null es "object" en JavaScript. Añade siempre una comprobación explícita de null.
interface Cat { meow(): void }
interface Dog { bark(): void }
function speak(animal: Cat | Dog): void {
if ("meow" in animal) {
animal.meow();
} else {
animal.bark();
}
}Cuando la lógica de la guarda se vuelve reutilizable o demasiado compleja para el análisis de flujo, escribe una type guard personalizada: una función cuyo tipo de retorno está anotado como value is T. Entonces el compilador confía en tu implementación. Es un contrato: si tu comprobación es incompleta, reintroduces errores en tiempo de ejecución que el sistema de tipos nunca verá. Desde TypeScript 5.5, los predicados simples se infieren automáticamente en callbacks como filter, pero la anotación explícita sigue siendo la norma para las funciones exportadas.
interface User { id: string; email: string }
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
typeof value.id === "string" &&
"email" in value &&
typeof value.email === "string"
);
}
const raw: unknown =
JSON.parse('{"id":"1","email":"a@b.c"}');
if (isUser(raw)) {
console.log(raw.email.toLowerCase());
}typeof value === "object" no basta para reducir a un objeto no nulo?booleanvalue as Uservalue is Userasserts value