TypeScript interface downcasting
Solution 1
You can cast your Animal
into a Horse
like this.
function rideAnimal(animal:Animal) {
var horse = animal as Horse;
horse.speed = 10;
rideHorse(horse);
}
Solution 2
Yes, you can use user defined type guards to handle this kind of custom type evaluation:
interface Animal
{
age: number;
//... 100 other things
}
interface Horse extends Animal
{
speed: number;
}
module AnimalModule
{
function rideHorse(horse: Horse)
{
alert(horse.speed);
}
function isHorse(a: Animal): a is Horse
{
return "speed" in a;
}
export function rideAnimal(animal: Animal)
{
if (isHorse(animal))
{
animal.speed = 10; // Ok
rideHorse(animal); // Ok
} else
throw new Error("You can only ride horses")
}
}
If Horse
and Animal
were classes, you could have just done if (animal instanceof Horse)
instead of using the custom type guard isHorse
Duncan Lukkenaer
Updated on July 23, 2022Comments
-
Duncan Lukkenaer almost 2 years
I have an interface which is extending another one. Now I am trying to put a variable of the superinterface type into a function that needs a subinterface type parameter. This is not possible as the superinterface typed variable is missing certain attributes.
It's a little hard to explain, so I've created an example with an
Animal
interface, which is extended by aHorse
interface:interface Animal { age:number; //... 100 other things } interface Horse extends Animal { speed:number; }
Now I have a module with one private function (
rideHorse
), which only accepts aHorse
as parameter. And a public function (rideAnimal
), accepting allAnimal
s like below:module AnimalModule { function rideHorse(horse:Horse) { alert(horse.speed); } export function rideAnimal(animal:Animal) { animal.speed = 10; // Error: Animal cannot have speed rideHorse(animal); // Error: 'animal' needs speed } } // Calling the rideAnimal function var myAnimal:Animal = { age: 10 }; AnimalModule.rideAnimal(myAnimal);
As you can see this is not working because the
animal
parameter ofrideAnimal
doesn't havespeed
. So my question is: How can I cast myanimal
into aHorse
and addspeed
manually inside therideAnimal
function, so the errors will disappear? -
Duncan Lukkenaer about 8 yearsI know it shouldn't be possible to ride an
Animal
withoutspeed
, that's why I want to addspeed
to eachanimal
object insiderideAnimal
. -
Duncan Lukkenaer about 8 yearsAlthough in my situation every
Animal
should become aHorse
, so wouldn't such an if-statement be unnecessary? Also, this doesn't get rid of the errors. -
ArcSine about 8 yearsUpdated the code to use generics to hopefully be a little more robust, but honestly if you are looking for certain properties you might want to consider structural typing. E.g. only call the rideHorse method if the animal has a speed property vs what class the animal belongs to.
-
Regis Portalez about 8 yearsCan you ride a worm? Your question makes no sense. You can still cast your animal to any and add the speed property to it. But it's ugly design
-
Duncan Lukkenaer about 8 yearsI get your point, but in my case you can assume that the
animal
is ridable, but is just missing thespeed
property. Sorry about my example not accurately reflecting the real world. -
Duncan Lukkenaer about 8 yearsThis seems to be the simplest solution to achieve what I want. Although there will now be no warning if you don't add
speed
to thehorse
, sospeed
can beundefined
. But I suppose that's a small price to pay. -
Valéry about 8 yearsIf you want more safety, use a conversion function
toHorse
that takes anAnimal
, casts it to aHorse
and returns it after adding aspeed
. -
trusktr almost 4 yearsOr add conditional checks to check that the animal is a horse (throw otherwise). I think that's the best way.