6
votes

I want to overload an abstract method within an abstract class like this:

abstract class Animal {

    public abstract communicate(sentence: string): void;
    public abstract communicate(notes: string[]): void;

}

class Human extends Animal {

    public communicate(sentence: string): void {
        // Do stuff
    }

}

class Bird extends Animal {

    public communicate(notes: string[]): void {
        // Do stuff
    }

}

However, Typescript gives an error, stating I incorrectly extend the base class (Animal)

Is there something I am doing wrong? Like expecting something to work which would work anyway according to OOP? Or is it something Typescript doesn't support?

Note: The type of the parameter can be entirely different types unlike in this example.

3
Check out this question stackoverflow.com/questions/12688275/method-overloading. Looks like when Typescript transpiles to JS, having two methods with the same signature is invalid, even if they have different params. - zigzag
It might be useful to see what TypeScript produces as output js code - typescriptlang.org/play - Bakudan

3 Answers

4
votes

Like @DavidSherret said the sub-class must implement all the abstract class overloads, not just one of them. So you won't really be able to do what you want using abstract class overloads.

Since what you want is to have Bird and Human have a different communicate() param type, enforced by the compiler, I would use a generic type argument with type constraint:

abstract class Animal<C extends string | string[]> {
    public abstract communicate(communication: C): void;
}

class Human extends Animal<string> {
    public communicate(sentence: string): void { }
}

class Bird extends Animal<string[]> {
    public communicate(notes: string[]): void { }
}

class Bynar extends Animal<boolean> { // Error: 'boolean' does not satisfy the constraint 'string | string[]'.
    public communicate(bit: boolean): void { }
}

const human = new Human();
human.communicate("hello"); // OK
human.communicate(["hello"]); // Error

const bird = new Bird();
bird.communicate("hello"); // Error
bird.communicate(["hello"]); // OK

Tip: you can make use of TS 2.3 default type arguments here as well.

3
votes

That abstract class expects two method signatures to be implemented. Those two method signatures being:

public abstract communicate(sentence: string): void;
public abstract communicate(notes: string[]): void;

They can be implemented like so:

class Human extends Animal {
    communicate(sentence: string); // optional overload signature
    communicate(notes: string[]);  // optional overload signature
    communicate(sentenceOrNotes: string | string[]) {
        // Do stuff
    }
}

The final signature there is the implementation signature. It needs to be compatible with the method signatures that need to be implemented.

Note on Child Classes

Child classes need to be compatible with the base so that when you do...

const animal: Animal = new Bird();

...the child class should be able to handle both calls:

animal.communicate("some sentence");
animal.communicate(["notes", "more notes"]);

In this case it might be more appropriate to create separate interfaces based on the form of communication (or use mixins):

interface CommunicatesWithSentences {
    communicate(sentence: string);
}

class Human extends Animal implements CommunicatesWithSentences {
    communicate(sentence: string) {
        // Do stuff
    }
}
0
votes

you can do it like this, I hope this is useful for you guys.

export abstract class BaseComponent {

     constructor(protected valueName: string) {

     }

     protected doSomethingMoreButOptional(config: IConfig): void { }

     protected doSomething() {
         if (valueName === 'derived') {
             this.doSomethingMoreButOptional(new Config());
         }
     }
}

export class DerivedComponent extends BaseComponent {

    private _xxx: any[];
    private _yyy: any[];

    constructor() {
        super('derived');
        super.doSomething();
    }

    protected doSomethingMoreButOptional(config: IConfig): void {
        switch (config.ABC) {
           case 'xxx':
              config.options = this._xxx;
              break;

           case 'yyy':
              config.options = this._yyy;
              break;

           default:
              return;
        }
    }
}