Typescript känner inte igen egenskaper på basklassen?


Jag har ett grundläggande arv på följande sätt:

export abstract class Animal
{
     constructor() {

     }

     makeSound() {
         alert(this.sound);
     }
}

export class Dog extends Animal
{
     public sound: string = "Woof!";

     constructor() {
         super();
     }
}

const dog = new Dog;
dog.makeSound(); // Woof!

Jag har ett problem med att få ovanstående kodsnutt att fungera. Property prop does not exist on type Animal. Den finns dock på Dog, och eftersom den är publik borde Animal kunna nå prop via this.prop.

Jag försökte följande:

  • Samma som ovan - Det kompilerar inte. (Egenskapen prop finns inte på typen Animal)
  • Om jag lägger till public prop: any; till Animal, då blir prop undefined och inte Hello world.
  • Om jag lägger till public abstract prop: any; i Animal, händer samma sak.

Hela poängen med arv genom att använda abstrakta klasser är att dela this-omfånget.

Hur kan jag göra det korrekt med Typescript?

Svar #1

Jag håller med JB Nizet. Men du har också möjligheten att definiera sound som en abstrakt egenskap. På så sätt vet din basklass om sound, och alla klasser som ärver måste implementera den abstrakta egenskapen sound:

export abstract class Animal
{
    abstract sound: string; // Only change here, now your code works
    constructor() {

    }

    makeSound() {
        alert(this.sound);
    }
}

export class Dog extends Animal {
    public sound: string = "Woof!";

    constructor() {
        super();
    }
}

const dog = new Dog;
dog.makeSound(); // Woof!

Möjligheten att definiera abstrakta egenskaper kom med TS 2.0: https://github.com/Microsoft/TypeScript/issues/4669

Uppdatering

Problemet som OP upplevde med detta tillvägagångssätt, se kommentarer, var att den abstrakta egenskapen användes i konstruktorn för den abstrakta basklassen. Detta fungerar inte eftersom egenskapen får sitt initialvärde från den härledda klassen först efter att objektet har instansierats.

Det finns ett GitHub-ärende för detta problem, där lösningen helt enkelt är att ge ett fel vid åtkomst till abstrakta egenskaper inom konstruktorn för den abstrakta klassen.

Svar #2

Så här bör du definiera dina två klasser:

export abstract class Animal {

  protected constructor(public sound: string) {
  }

  makeSound() {
    alert(this.sound);
  }
}

export class Dog extends Animal {
  constructor() {
    super('woof');
  }
}

Att lägga till egenskapen i Animal-klassen berättar för kompilatorn att alla djur har ett ljud. Att använda ljudet som ett argument i superklasskonstruktorn gör det tydligt att alla subklasser måste specificera sitt ljud när de skapar instansen.

Det finns andra möjliga variationer, men om superklassen behöver komma åt ljudegenskapen eller någon getSound()-metod, då bör denna egenskap/metod finnas i basklassen.

Relaterade artiklar