Debo disculparme si esta pregunta está duplicada, pero primero busqué en Google sin obtener una respuesta útil.
En mecanografiado, tengo una clase base:
abstract class Animal { // Animal class definition here // ... } Y tengo varias subclases que heredan de la clase Animal :
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.') } // ... } ¿Cómo puedo declarar una variable cuyo tipo es la subclase de Animal en mecanografiado?
así, pero no funciona el código:
let animal: any extends Animal = new Cat(); animal.jump(); // typescript will warn me: "Property 'jump' does not exist on type 'Animal'"o así en Java, que es el código de trabajo de Java:
// java code Animal animal = new Cat(); animal.jump(); let animal: Cat | Dog = new Cat(); no es una buena respuesta, porque puedo implementar más subclases que se extiendan desde la clase Animal en el futuro.
Además, no quería simplemente declarar una variable como lo hice anteriormente (en ese caso, puedo usar let animal = new Cat(); ), de hecho, este es el escenario real que he encontrado:
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); Además, no puedo cambiar la definición de la clase Cat o Dog porque son de mi importación externa (carpeta node_modules).
Estas son tus opciones:
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();Esto tiene el beneficio adicional de que puede usar tipos de intersección para especificar varias 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 errorO alguna combinación de las anteriores.
Tienes un problema de diseño. Te falta un tipo intermedio: HouseAnimal (o Pet ). Su diseño real debería ser algo como esto:
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 }Este es probablemente el enfoque más correcto usando la herencia. Alternativamente, también podría hacer que los animales domésticos tengan ciertas características que tienen los animales que no son domésticos si no desea un tipo intermedio. Para hacer cumplir esto en el sistema de tipos, puede usar una interfaz:
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 }