TypeScript interface downcasting

10,983

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

Share:
10,983
Duncan Lukkenaer
Author by

Duncan Lukkenaer

Updated on July 23, 2022

Comments

  • Duncan Lukkenaer
    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 a Horse 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 a Horse as parameter. And a public function (rideAnimal), accepting all Animals 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 of rideAnimal doesn't have speed. So my question is: How can I cast my animal into a Horse and add speed manually inside the rideAnimal function, so the errors will disappear?

  • Duncan Lukkenaer
    Duncan Lukkenaer about 8 years
    I know it shouldn't be possible to ride an Animal without speed, that's why I want to add speed to each animal object inside rideAnimal.
  • Duncan Lukkenaer
    Duncan Lukkenaer about 8 years
    Although in my situation every Animal should become a Horse, so wouldn't such an if-statement be unnecessary? Also, this doesn't get rid of the errors.
  • ArcSine
    ArcSine about 8 years
    Updated 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
    Regis Portalez about 8 years
    Can 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
    Duncan Lukkenaer about 8 years
    I get your point, but in my case you can assume that the animal is ridable, but is just missing the speed property. Sorry about my example not accurately reflecting the real world.
  • Duncan Lukkenaer
    Duncan Lukkenaer about 8 years
    This 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 the horse, so speed can be undefined. But I suppose that's a small price to pay.
  • Valéry
    Valéry about 8 years
    If you want more safety, use a conversion function toHorse that takes an Animal, casts it to a Horse and returns it after adding a speed.
  • trusktr
    trusktr almost 4 years
    Or add conditional checks to check that the animal is a horse (throw otherwise). I think that's the best way.