javascript - Hur deklarerar man en variabels typ som en klass som ärver/från en basklass i typescript?
Jag måste be om ursäkt om denna fråga är duplicerad, men jag googlade först utan att få ett användbart svar.
Typescript är ganska nytt för mig, idag har jag stött på ett problem med det.
Scenario
I TypeScript har jag en basklass:
abstract class Animal {
// Animal class definition here
// ...
}
Och jag har flera underklasser som ärver från Animal-klassen:
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.') }
// ...
}
Fråga
Hur kan jag deklarera en variabel vars typ är en underklass av Animal i TypeScript?
som detta, men det är inte fungerande kod:
let animal: any extends Animal = new Cat();
animal.jump(); // typescript will warn me: "Property 'jump' does not exist on type 'Animal'"
eller så här i Java, vilket är Javas fungerande kod:
// java code
Animal animal = new Cat();
animal.jump();
Kuriosa
let animal: Cat | Dog=new Cat(); är inte ett bra svar, eftersom jag kan komma att implementera fler subklasser som ärver från Animal-klassen i framtiden.
Dessutom ville jag inte bara deklarera en variabel som jag gjorde ovan (i så fall kan jag bara använda let animal=new Cat();), i själva verket är detta det faktiska scenario jag har stött på:
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);
Dessutom kan jag inte ändra definitionen av Cat eller Dog-klassen eftersom de är från mitt externa import (node_modules-mappen).
Svar #1
Det här är dina alternativ:
1. Lägg till ytterligare typer i typ-hierarkin
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();
2. Använd gränssnitt för att definiera gemensamt beteende
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();
Detta har den ytterligare fördelen att du kan använda intersecting-typer för att specificera flera gränssnitt:
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();
3. Använd unionstyper för att skapa typkategorier
type JumpingAnimal = Dog | Cat;
const cat: JumpingAnimal = new Cat();
cat.jump();
4. Använd typvakter för att begränsa typen
const cat: Animal = new Cat();
if (cat instanceof Cat) {
cat.jump(); // no error
}
5. Använd typbestämningar för att tala om för kompilatorn: "Lita på mig, jag vet vad jag gör"
const cat: Animal = new Cat();
(cat as Dog).swim(); // runtime error
Eller någon kombination av ovanstående.
Svar #2
Du har ett designproblem. Du saknar en mellanliggande typ: HouseAnimal (eller Pet). Din faktiska design bör vara något i stil med detta:
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
}
Detta är förmodligen det mest korrekta tillvägagångssättet med arv. Alternativt kan du också låta husdjur ha vissa egenskaper som icke-husdjur har om du inte vill ha en mellanliggande typ. För att säkerställa detta i typ systemet kan du använda ett gränssnitt:
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
}
