Based on these comments, I wrote this
class AbstractClass {
constructor() {
if(new.target === AbstractClass || this.__proto__.__proto__.constructor === AbstractClass)
throw new TypeError("Cannot construct "+ this.constructor.name + " class instances directly");
let exceptions = {};
let currProto = this;
while(currProto.constructor !== AbstractClass ) {
for(let method of (currProto.constructor.abstractMethods || [])) {
if("function" !== typeof(this[method]))
exceptions[method] = currProto.constructor.name;
}
currProto = currProto.__proto__;
}
if(0 !== Object.keys(exceptions).length) {
let exceptionsArray = [];
for(let method in exceptions) {
exceptionsArray.push( exceptions[method] + "." + method);
}
exceptionsArray.sort();
throw new TypeError("Must override the following methods: " + exceptionsArray.join(", "));
}
}
}
Usage:
class MyAbstractClass1 extends AbstractClass {
static abstractMethods = [
"myMethod1", // (x:string, y:string): string
"myMethod2" // (y:string, z:string): string
]
}
class MyAbstractClass2 extends MyAbstractClass1 {
static abstractMethods = [
"myMethod3", // (x:string, y:string): string
"myMethod4" // (y:string, z:string): string
]
}
class MyClass extends MyAbstractClass2 {
myMethod1(x, y){return "apple"}
}
new MyClass()
//Error