Historia de la pregunta:
Con el desarrollo de TypeScript, surgió la necesidad de determinar de manera confiable un tipo más estrecho de variable en ramas lógicas. Las comprobaciones de tipo clásicas (a través de typeof o instanceof) no siempre son suficientes, especialmente si un objeto tiene una estructura o jerarquía compleja. Para aumentar la seguridad de los datos y la conveniencia, TypeScript implementó el mecanismo de predicados de tipo para crear guardias de tipo personalizadas.
Problema:
Las comprobaciones de tipo comunes dentro de las funciones no proporcionan al compilador información sobre el tipo de variable en el código posterior, si solo se utiliza el resultado true/false. El compilador no “entiende” qué se ha comprobado. Esto lleva a errores implícitos en tiempo de ejecución, cuando accidentalmente accedemos a propiedades inexistentes.
Solución:
Los tipos de predicados (type predicates) mediante el tipo de sustitución en la forma de 'param is Type' permiten que el compilador entienda que con este parámetro, después de llamar a la comprobación, se puede trabajar como con un tipo determinado. Estas funciones aumentan la seguridad de tipos y amplían el sistema de restricción de tipos para cualquier tarea compleja.
Ejemplo de código:
interface Bird { fly(): void; feathers: boolean; } interface Fish { swim(): void; fins: number; } function isBird(animal: Bird | Fish): animal is Bird { return (animal as Bird).fly !== undefined; } const pet: Bird | Fish = ...; if (isBird(pet)) { pet.fly(); // OK: pet ahora es Bird } else { pet.swim(); // OK: pet ahora es Fish }
Características clave:
¿Puede una función de guardia de tipo funcionar si no se indica explícitamente el tipo de retorno 'param is Type' en la firma?
No, si no se indica explícitamente 'param is Type' en la firma, TypeScript no podrá restringir el tipo en las ramas de código, a pesar del valor de retorno true/false. El compilador no entenderá que el parámetro se puede utilizar como un tipo determinado.
Ejemplo de código:
function isFish(animal: Fish | Bird): boolean { return (animal as Fish).swim !== undefined; } // ¿Funciona? if (isFish(pet)) { pet.swim(); // Error: Property 'swim' does not exist }
¿Se pueden usar los predicados de tipo para comprobar valores primitivos, como una cadena o un número?
Sí, se puede, pero a menudo se utiliza typeof y esas guardias se vuelven redundantes. Sin embargo, nada impide la implementación de una guardia personalizada:
function isString(x: unknown): x is string { return typeof x === "string"; }
¿Proporciona la función de guardia de tipo una protección estricta contra errores de tipo en el momento de la compilación?
No completamente. TypeScript confía en la implementación de la propia función y no puede verificar la corrección de la lógica dentro de ella. Si implementas incorrectamente la comprobación, el compilador no entenderá el error, y surgirán problemas en tiempo de ejecución.
function isFish(animal: Fish | Bird): animal is Fish { // Erróneamente: siempre devuelve true return true; }
** Caso negativo Un desarrollador implementó una función de predicado, pero cometió un error en la verificación de la estructura, lo que hizo que la función siempre devolviera true. El código pasó la compilación, pero en tiempo de ejecución se produjo una llamada a un método inexistente.
Pros:
Contras:
** Caso positivo Las funciones de predicado de tipo se implementaron correctamente y se probaron con pruebas unitarias en límites y datos erróneos.
Pros:
Contras: