31

I'm using an interface in TypeScript to define a function that is only available on some of the classes that extend the base class. This is a simplified version of the code I have so far:

class Animal {
}

interface IWalkingAnimal {
    walk(): void;
}

class Dog extends Animal implements IWalkingAnimal {
}

class Cat extends Animal implements IWalkingAnimal {
}

class Snake extends Animal {
}

private moveAnimal(animal: Animal) {
    if (animal instanceof Cat || animal instanceof Dog) {
        animal.walk();
    }
}

Now, the trouble is I'll be adding more 'walking' animals so the moveAnimal functional will grow too large to be manageable. What I would like to do is something like this:

private moveAnimal(animal: Animal) {
    if (animal implements IWalkingAnimal ) {
        animal.walk();
    }
}

However the 'implements' check does not work, and I cannot find an equivalent to 'instanceof' when using interfaces. In Java it seems that the use of 'instanceof' would work here, but TypeScript will not allow this.

Does such a thing exist in TypeScript, or is there a better approach here? I am using the TypeScript 1.8.9.

wonea
  • 4,783
  • 17
  • 86
  • 139
MarkLunney
  • 425
  • 1
  • 4
  • 10
  • 1
    why don't you have `IWalkingAnimal` being a subclass of Animal instead an interface? That way you won't have the problem and it makes sense – iberbeu May 31 '16 at 10:52
  • 1
    @iberbeu That would work for this simplified example, for flexibility I would like IWalkingAnimal to be an interface so it is not strictly tied to Animal. Thilo's answer below should do the trick. – MarkLunney May 31 '16 at 13:06
  • 1
    Possible duplicate of [Interface type check with Typescript](https://stackoverflow.com/questions/14425568/interface-type-check-with-typescript) – Krisztián Balla Nov 24 '17 at 08:05

1 Answers1

36

Unlike classes, interfaces exist only at compile-time, they are not included into the resulting JavaScript, so you cannot do an instanceof check.

You could make IWalkingAnimal a subclass of Animal (and use instanceof), or you could check if the object in question has a walk method:

if (animal['walk']) {}

You can wrap this in a user defined type guard (so that the compiler can narrow the type when used in an if statement, just like with instanceof).

/**
* User Defined Type Guard!
*/
function canWalk(arg: Animal): arg is IWalkingAnimal {
   return (arg as IWalkingAnimal).walk !== undefined;
}


private moveAnimal(animal: Animal) {
    if (canWalk(animal)) {
        animal.walk();  // compiler knows it can walk now
    }
}
Diego Juliao
  • 414
  • 6
  • 16
Thilo
  • 257,207
  • 101
  • 511
  • 656
  • 5
    I suggest to do `(arg as IWalkingAnimal).walk` so that if you decide to rename the walk method to something else then the code in the type guard will change as well. – David Sherret May 31 '16 at 14:17
  • Is this type check bettter then the `instanceof` subclass option?—OO design wise? – Sukima Feb 17 '23 at 00:14