Reputation: 69
I must apologize if this question is duplicated, but I googled first without getting a useful answer.
In typescript, I have a base class:
abstract class Animal {
// Animal class definition here
// ...
}
And I have several sub-classes that inherits from Animal
class:
class Cat extends Animal {
// Cat class definition here
jump() { console.log('Cat jumps high like a cat.') }
// ...
}
class Dog extends Animal {
// Dog class definition here
swim() { console.log('Dog swims like a dog.') }
// ...
}
How can I declare a variable whose type is the sub-class of Animal in typescript?
like this, but it is not working code:
let animal: any extends Animal = new Cat();
animal.jump(); // typescript will warn me: "Property 'jump' does not exist on type 'Animal'"
or like this in java, which is java's working code:
// java code
Animal animal = new Cat();
animal.jump();
let animal: Cat | Dog = new Cat();
is not a good answer, because I may implement more sub-classes that extends from Animal
class in the future.
Also, I did not merely want to declare a variable like I did above (in that case, I can just use let animal = new Cat();
), in fact, this is the actual scenario I have encountered:
class House {
private animalThatStaysIn; // what type should I declare for this member? I think it is the class that inherits/extends from Animal
constructor(animal) {
this.animalThatStaysIn = animal;
}
}
let cat = new Cat();
let catHouse = new House(cat);
let dog = new Dog();
let dogHouse = new House(dog);
What's more, I cannot change the definition of Cat
or Dog
class for they are from my external import(node_modules folder).
Upvotes: 2
Views: 1687
Reputation: 97152
These are your options:
abstract class Animal {}
abstract class JumpingAnimal {
abstract jump(): void;
}
abstract class SwimmingAnimal {
abstract swim(): void;
}
class Cat extends JumpingAnimal {
jump() { console.log('Jumping like a cat'); }
}
class Fish extends SwimmingAnimal {
swim() { console.log('Swimming like a fish'); }
}
const cat: JumpingAnimal = new Cat();
cat.jump();
abstract class Animal {}
interface Jumper {
jump: () => void;
}
interface Swimmer {
swim: () => void;
}
class Cat extends Animal implements Jumper {
jump() { console.log('Jumping like a cat'); }
}
class Fish extends Animal implements Swimmer {
swim() { console.log('Swimming like a fish'); }
}
const cat: Animal & Jumper = new Cat();
cat.jump();
This has the additional benefit that you can use intersection types to specify multiple interfaces:
class Dog extends Animal implements Jumper, Swimmer {
jump() { console.log('Jumping like a cat'); }
swim() { console.log('Swimming like a fish'); }
}
const dog: Animal & Jumper & Swimmer = new Dog();
dog.jump();
dog.swim();
type JumpingAnimal = Dog | Cat;
const cat: JumpingAnimal = new Cat();
cat.jump();
const cat: Animal = new Cat();
if (cat instanceof Cat) {
cat.jump(); // no error
}
const cat: Animal = new Cat();
(cat as Dog).swim(); // runtime error
Or some combination of the above.
Upvotes: 2
Reputation: 113906
You have a design problem. You are missing an intermediate type: HouseAnimal
(or Pet
). Your actual design should be something like this:
abstract class Animal {
// Animal class definition here
// ...
}
abstract class HouseAnimal extends Animal {
// Features only house animals have
}
class Cat extends HouseAnimal {}
class Dog extends HouseAnimal {}
class House {
private animals: HouseAnimal
}
This is probably the most correct approach using inheritance. Alternatively you could also make house animals have certain features that non-house-animals have if you don't want an intermediate type. To enforce this in the type system you can use an interface:
abstract class Animal {
// Animal class definition here
// ...
}
interface HouseAnimal {
// These animals can be in houses
}
class Cat extends Animal implements HouseAnimal {}
class Dog extends Animal implements HouseAnimal {}
class House {
private animals: HouseAnimal
}
Upvotes: 0