I've been studying User-Defined Type Guards in the Typescript handbook. Imagine you have a union type that needs to be narrowed down like this:
interface Bird{
fly();
layEggs();
}
interface Fish{
swim();
layEggs();
}
class SmallPet implements Fish,Bird{
constructor(){
}
fly() { console.log("fly")};
swim() { console.log("swim")};
layEggs() {console.log("laying eggs") };
}
function getSmallPet(): Fish | Bird{
return new SmallPet();
}
function isFish(pet: Fish | Bird): pet is Fish{
return (<Fish>pet).swim !== undefined;
}
let pet = getSmallPet();
if (isFish(pet))
pet.swim(); //works
The function isFish acts as a User-Defined Type Guard, according to the handbook. I'm curious about how this mechanism operates. I attempted a less precise method that didn't yield the desired outcome:
pet is Fish;
pet.swim(); //doesn't work
Does Typescript need to interpret a function resembling a type guard to enable this functionality and subsequently refine the type with the function call? Are there any alternative approaches to implementing type guards?