Extending vs. implementing a pure abstract class in TypeScript
Asked Answered
C

5

118

Suppose I have a pure abstract class (that is, an abstract class without any implementation):

abstract class A {
    abstract m(): void;
}

Like in C# and Java, I can extend the abstract class:

class B extends A {
    m(): void { }
}

But unlike in C# and Java, I can also implement the abstract class:

class C implements A {
    m(): void { }
}

How do classes B and C behave differently? Why would I choose one versus the other?

(Currently, the TypeScript handbook and language specification don't cover abstract classes.)

Chancellorship answered 14/3, 2016 at 14:40 Comment(2)
There is now a section on Abstract Classes in the TypeScript handbook. typescriptlang.org/docs/handbook/classes.html Thanks for this question! I had no idea abstract was available in TS and this helped me understand it.Autocatalysis
Pretty amazing that the docs linked by @Autocatalysis somehow don't have a single example of class C implements A. I didn't even know it was possible until reading these posts, and I've been in the ts docs a lot lately.Rhinitis
P
155

The implements keyword treats the A class as an interface, that means C has to implement all the methods defined in A, no matter if they have an implementation or not in A. Also there are no calls to super methods in C.

extends behaves more like what you'd expect from the keyword. You have to implement only the abstract methods, and super calls are available/generated.

I guess that in the case of abstract methods it does not make a difference. But you rarely have a class with only abstract methods, if you do it would be much better to just transform it to an interface.

You can easily see this by looking at the generated code. I made a playground example here.

Proclivity answered 14/3, 2016 at 14:52 Comment(4)
If A is a pure abstract class, perhaps the only observable difference is the behavior of the instanceof keyword? I agree that interfaces would probably be preferable to pure abstract classes. However, in Angular 2, only the latter can be used as DI provider tokens.Chancellorship
indeed, thanks for spotting the instanceof thing, I'll add it to the answer for completion (you can make an edit if you want). As for angular2, I guess that's an angular2 specific issue. :)Proclivity
@toskv, thanks for describing the applicability of the 'implements' keyword to interfaces AND class. For example, AngularJS 2.0 uses this to implement their 'OnInit' CLASS (rather than 'interface'). I have looked around, do you have a reference to this. I don't see it clearly in the TS documentation.Baguette
@Proclivity Found the reference in github @ github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3.9.4.Baguette
P
36

I was led here because I had just been asking myself the same question and while reading the answers it ocurred to me that the choice will also affect the instanceof operator.

Since an abstract class is an actual value that gets emitted to JS it can be used for runtime checks when a subclass extends it.

abstract class A {}

class B extends A {}

class C implements A {}

console.log(new B() instanceof A) // true
console.log(new C() instanceof A) // false
Postfree answered 15/2, 2018 at 8:30 Comment(1)
Thanks @Postfree this is the pure and well explained one. Also I want to add extra info for this one, abtract or not is not important if you remove the abstract keywords you will get the same result.Mallen
J
16

Building on @toskv's answer, if you extend an abstract class, you have to call super() in the subclass's constructor. If you implement the abstract class, you don't have to call super() (but you have to implement all the methods declared in the abstract class, including private methods).

Implementing an abstract class instead of extending it could be useful if you want to create a mock class for testing without having to worry about the original class's dependencies and constructor.

Jewell answered 16/1, 2018 at 23:55 Comment(0)
M
0

In the example of extends that you give you don't actually add anything new to the class. So it is extended by nothing. Although extending by nothing is valid Typescript it would seem to me that in this case 'implements' would be more appropriate. But at the end of the day they are equivalent.

Monde answered 15/6, 2017 at 11:41 Comment(0)
T
0

i've found that using a pure abstract class ( A ) and then using implements seems to be a way to annotate information about the access privileges that you might want to force in the contract, but Interfaces or base class extending doesn't

For example, this works for me ( excerpt from a Svelte-Kit program for reference only )

abstract class AudioEngine {
    protected static ctx: AudioContext;
    protected static setState(s: string) { };
}

class Elementary implements AudioEngine {

constructor(ctx: AudioContext) {
    this.ctx = ctx;
};

ctx: AudioContext

protected setState(newState: string) {
        this.status.set(Sound[newState])
        return this.getState()
    };
Topmast answered 13/3, 2022 at 12:41 Comment(1)
it is also possible to experiment with both ... class Elementary extends AudioEngine implements AudioEngine {...Topmast

© 2022 - 2024 — McMap. All rights reserved.