Skip to content

Instantly share code, notes, and snippets.

@Zodiase
Last active July 18, 2022 11:03
Show Gist options
  • Save Zodiase/af44115098b20d69c531 to your computer and use it in GitHub Desktop.
Save Zodiase/af44115098b20d69c531 to your computer and use it in GitHub Desktop.
ES6 Abstract Class Example
'use strict';
class Abstract {
// A static abstract method.
static foo() {
if (this === Abstract) {
// Error Type 2. Abstract methods can not be called directly.
throw new TypeError("Can not call static abstract method foo.");
} else if (this.foo === Abstract.foo) {
// Error Type 3. The child has not implemented this method.
throw new TypeError("Please implement static abstract method foo.");
} else {
// Error Type 5. The child has implemented this method but also called `super.foo()`.
throw new TypeError("Do not call static abstract method foo from child.");
}
}
constructor() {
if (this.constructor === Abstract) {
// Error Type 1. Abstract class can not be constructed.
throw new TypeError("Can not construct abstract class.");
}
//else (called from child)
// Check if all instance methods are implemented.
if (this.foo === Abstract.prototype.foo) {
// Error Type 4. Child has not implemented this abstract method.
throw new TypeError("Please implement abstract method foo.");
}
}
// An abstract method.
foo() {
// Error Type 6. The child has implemented this method but also called `super.foo()`.
throw new TypeError("Do not call abstract method foo from child.");
}
}
// Error Type 1.
//let bar = new Abstract(); // Throws because abstract class can not be constructed.
// Error Type 2.
//Abstract.foo(); // Throws because static abstract methods can not be called.
class ChildA extends Abstract {}
// Error Type 3.
//ChildA.foo(); // Throws because ChildA does not implement static abstract method foo.
// Error Type 4.
//let bar = new ChildA(); // Throws because ChildA does not implement abstract method foo.
class ChildB extends Abstract {
static foo() {
// Calls Abstract.foo();
super.foo();
}
foo() {
// Calls Abstract.prototype.foo();
super.foo();
}
}
// Error Type 5.
//ChildB.foo(); // Throws because in ChildB the implementation calls the static abstract method foo.
// Error Type 6.
//(new ChildB()).foo(); // Throws because in ChildB the implementation calls the abstract method foo.
class ChildC extends Abstract {
static foo() {
// Implementation of abstract static method.
console.log('ChildC.foo');
}
constructor() {
super();
// Implementation of constructor.
}
foo() {
// Implementation of abstract method.
console.log('ChildC.prototype.foo');
}
}
// Success.
ChildC.foo();
let bar = new ChildC();
bar.foo();
@Zodiase
Copy link
Author

Zodiase commented Feb 11, 2016

Unfortunately this pattern does not warn you about the errors when defining the children, but only warn you when you use the methods.

@lambduli
Copy link

Hey Zodiase, why don't you use new.target insead? Are there some edge cases?

@gormat
Copy link

gormat commented Nov 2, 2016

@taskkill new.target is not supported in safari.
new.target is good method if u will use it in node.js.

@Zodiase
Copy link
Author

Zodiase commented Jan 17, 2017

@taskkill OMG I just realized Gist doesn't support notifications and I didn't see your question until I was scanning through my gists today. Ironically, since there's no notification, you probably wouldn't see this reply either (just guessing). But yeah as @gormat pointed out, new.target is not well supported.
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment