在 TypeScript 中扩展与实现纯抽象类

假设我有一个纯抽象类(也就是说,一个没有任何实现的抽象类) :

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

就像在 C # 和 Java 中,我可以使用 延伸的抽象类:

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

但是在 C # 和 Java 中的 不像中,我也可以使用 执行的抽象类:

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

BC的行为有什么不同? 为什么我会选择其中一个而不是另一个?

(目前,TypeScript手册语言规范语言规范不包括抽象类。)

48776 次浏览

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.

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.

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.

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

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()
};