Does ECMAScript 6 have a convention for abstract classes?

98,702

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
Share:
98,702

Related videos on Youtube

obelia
Author by

obelia

Getting back into web development after some time away from the software biz. I've been a usability advocate for a long time. Fan of Alan Cooper, Jakob Nielsen and Steve Krug.

Updated on October 08, 2020

Comments

  • obelia
    obelia over 3 years

    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?

    • Pointy
      Pointy about 9 years
      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.
    • gorgi93
      gorgi93 about 9 years
      Since javascript is not strongly typed abstract classes would not be useful.
    • Jonathan Lonowski
      Jonathan Lonowski about 9 years
      Abstract classes, along with traits and mixins, are a pending "strawman" proposal.
    • obelia
      obelia about 9 years
      @gorgi93 - abstract classes have nothing to do with strong typing. Dynamically typed languages like Smalltalk have had abstract classes (by convention) since the 1970s.
    • gorgi93
      gorgi93 about 9 years
      @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?
    • obelia
      obelia about 9 years
      @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.
    • user5321531
      user5321531 about 9 years
      I will sometimes use this technique 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.
    • Matthew Pautzke
      Matthew Pautzke over 7 years
      (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). Then in your base class mymethod(){throw new NotImplementedError()}
    • theapache64
      theapache64 almost 6 years
  • obelia
    obelia almost 9 years
    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.
  • Domenic
    Domenic almost 9 years
    The other question was getting more and better answers, and is materially the same. Ah well.
  • siannone
    siannone almost 8 years
    Just discovered that new.target is not supported by Safari. The transpiled code throws: SyntaxError: Unexpected token '.'
  • Sámal Rasmussen
    Sámal Rasmussen over 7 years
    new.target is supported in latest babel as of 29. april 2016: github.com/babel/babel-eslint/issues/235
  • Sámal Rasmussen
    Sámal Rasmussen over 7 years
    However the explanation of new.target in this answer seems to be contradicted by this one: stackoverflow.com/a/32458960/628418
  • Jacob Phillips
    Jacob Phillips over 5 years
    @siannone MDN says safari supports new.target developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • Waruyama
    Waruyama over 3 years
    If you write throw new TypeError("Cannot construct " + new.target.name + " instances directly"); it will become more refactoring friendly.