I must apologize if this question is duplicated, but I googled first without getting a useful answer.
Typescript is quite new to me, today I have met a problem of it.
Scenario
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.') }
// ...
}
Question
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();
Trivia
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).
CodePudding user response:
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
}
CodePudding user response:
playground link: here
You can not jump on an animal because all animals are not able to jump. You need to explicitly specify that the animal you expect is type of cat. So you can basically make assumption that the animal is kind of animal that jumps (in this case it's cat):
abstract class Animal {
// Animal class definition here
// ... }
}
class Dog extends Animal {
// Dog class definition here
swim() { console.log('Dog swims like a dog.') }
// ...
}
class Cat extends Animal {
// Cat class definition here
jump() { console.log('Cat jumps high like a cat.') }
// ...
}
let animal: Animal = new Cat();
(animal as Cat).jump();
class House {
private animalThatStaysIn!:Animal; // what type should I declare for this member? I think it is the class that inherits/extends from Animal
constructor(animal: Animal) {
this.animalThatStaysIn = animal;
}
pet(){
(animal as Cat).jump();
}
}
let cat = new Cat();
let catHouse = new House(cat);
let dog = new Dog();
let dogHouse = new House(dog);