196

I was surprised that I couldn't find anything about abstract classes when reading up on ES6. (By "abstract class" I'm talking about the Java meaning of it, in which an abstract class declares method signatures that a subclass must implement in order to be instantiable).

Does anyone know of any conventions that have taken hold to implement abstract classes in ES6? It would be nice to be able to catch an abstract class violation with static analysis.

If I were to raise an error at runtime to signal an attempt at abstract class instantiation, what would the error be?

obelia
  • 2,105
  • 2
  • 13
  • 11
  • 5
    ES6 doesn't change the basic prototypal inheritance mechanism of earlier JavaScript versions. The concept of "abstract class" doesn't really make much sense in JavaScript terms, though a pre-processor type language could certainly implement such a thing. – Pointy Apr 06 '15 at 22:18
  • 1
    Since javascript is not strongly typed abstract classes would not be useful. – gorgi93 Apr 06 '15 at 22:32
  • 1
    Abstract classes, along with traits and mixins, are a pending ["strawman"](http://wiki.ecmascript.org/doku.php?id=strawman:strawman) [proposal](http://wiki.ecmascript.org/doku.php?id=strawman:trait_composition_for_classes). – Jonathan Lonowski Apr 06 '15 at 22:34
  • 6
    @gorgi93 - abstract classes have nothing to do with strong typing. Dynamically typed languages like Smalltalk have had abstract classes (by convention) since the 1970s. – obelia Apr 06 '15 at 23:16
  • @obella dont you create a custom type with abstract class that should then be implemented with inheritance? abstract classes or interfaces in js would be pointless since it is weakly typed. js has inheritance with prototypes and is not suitable for this. How am I wrong? – gorgi93 Apr 06 '15 at 23:25
  • @gorgi93 - I don't understand why you say abstract classes and interfaces would be pointless in weakly typed languages. Those conventions were developed in dynamically typed OOP languages long before strongly typed OOP languages existed. – obelia Apr 07 '15 at 00:31
  • I will sometimes use this technique http://usejsdoc.org/tags-abstract.html for documentation purposes (if you document dummy abstract methods then there is no need to document corresponding subclass methods, saves time). Since JS does not check that abstract methods have been implemented in subclasses the language has no need to declare abstract methods. – user5321531 Apr 07 '15 at 05:20
  • 2
    (late to the party) I'm going to side with you that this is not a duplicate question. It's going to be asked often even though it's the same beast underneath. JS classes are somewhat similar to that of ruby. I use a similar method to throw an error if something was not implemented, but do not check on instantiation (many ruby developers do the same). It's clear code in my opinion. Create a NotImplementedClass [(second answer)](http://stackoverflow.com/questions/31089801/extending-error-in-javascript-with-es6-syntax). Then in your base class `mymethod(){throw new NotImplementedError()}` – Matthew Pautzke Jan 26 '17 at 22:27
  • Use [typescript](https://www.typescriptlang.org/) – theapache64 Jul 15 '18 at 04:00

1 Answers1

368

ES2015 does not have Java-style classes with built-in affordances for your desired design pattern. However, it has some options which may be helpful, depending on exactly what you are trying to accomplish.

If you would like a class that cannot be constructed, but whose subclasses can, then you can use new.target:

class Abstract {
  constructor() {
    if (new.target === Abstract) {
      throw new TypeError("Cannot construct Abstract instances directly");
    }
  }
}

class Derived extends Abstract {
  constructor() {
    super();
    // more Derived-specific stuff here, maybe
  }
}

const a = new Abstract(); // new.target is Abstract, so it throws
const b = new Derived(); // new.target is Derived, so no error

For more details on new.target, you may want to read this general overview of how classes in ES2015 work: http://www.2ality.com/2015/02/es6-classes-final.html

If you're specifically looking for requiring certain methods be implemented, you can check that in the superclass constructor as well:

class Abstract {
  constructor() {
    if (this.method === undefined) {
      // or maybe test typeof this.method === "function"
      throw new TypeError("Must override method");
    }
  }
}

class Derived1 extends Abstract {}

class Derived2 extends Abstract {
  method() {}
}

const a = new Abstract(); // this.method is undefined; error
const b = new Derived1(); // this.method is undefined; error
const c = new Derived2(); // this.method is Derived2.prototype.method; no error
robsch
  • 9,358
  • 9
  • 63
  • 104
Domenic
  • 110,262
  • 41
  • 219
  • 271
  • 28
    thanks. But I don't think you should mark this as a duplicate because 1) it's a different question and 2) the accepted answer to the other question is the wrong answer to my question and 3) my question was asked first. I see AWB's answer (to the other question) answers my question here but isn't marked as the accepted answer. – obelia Jun 01 '15 at 05:00
  • The other question was getting more and better answers, and is materially the same. Ah well. – Domenic Jun 02 '15 at 00:58
  • 7
    Just discovered that `new.target` is not supported by Safari. The transpiled code throws: `SyntaxError: Unexpected token '.'` – siannone Jun 09 '16 at 09:53
  • 1
    new.target is supported in latest babel as of 29. april 2016: https://github.com/babel/babel-eslint/issues/235 – Sámal Rasmussen Aug 09 '16 at 07:31
  • However the explanation of new.target in this answer seems to be contradicted by this one: http://stackoverflow.com/a/32458960/628418 – Sámal Rasmussen Aug 09 '16 at 07:38
  • 1
    @siannone MDN says safari supports new.target https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target#Browser_compatibility – Jacob Phillips Nov 02 '18 at 21:26
  • 4
    If you write throw new TypeError("Cannot construct " + new.target.name + " instances directly"); it will become more refactoring friendly. – Waruyama Aug 08 '20 at 08:18