Inspect this.constructor
in the constructor of FinalClass
and throw if it is not itself. (Borrowing inspection of the this.constructor
instead of this.constructor.name
from @Patrick Roberts.)
class FinalClass {
constructor () {
if (this.constructor !== FinalClass) {
throw new Error('Subclassing is not allowed')
}
console.log('Hooray!')
}
}
class WrongClass extends FinalClass {}
new FinalClass() //=> Hooray!
new WrongClass() //=> Uncaught Error: Subclassing is not allowed
Alternatively, with support, use new.target
. Thanks @loganfsmyth.
class FinalClass {
constructor () {
if (new.target !== FinalClass) {
throw new Error('Subclassing is not allowed')
}
console.log('Hooray!')
}
}
class WrongClass extends FinalClass {}
new FinalClass() //=> Hooray!
new WrongClass() //=> Uncaught Error: Subclassing is not allowed
______
As you say, you could also achieve this behaviour with a decorator.
function final () {
return (target) => class {
constructor () {
if (this.constructor !== target) {
throw new Error('Subclassing is not allowed')
}
}
}
}
const Final = final(class A {})()
class B extends Final {}
new B() //=> Uncaught Error: Subclassing is not allowed
As Patrick Roberts shared in the comments the decorator syntax @final
is still in proposal. It is available with Babel and babel-plugin-transform-decorators-legacy.
final
ed as a premature performance hack. – CongealObject.freeze
can be used,Object.defineProperty
, etc., recursively even if need be (to 'deep freeze'). I just don't know why you would, in general, want to do that to a class other than performance and it would have to be one hell of a perf improvement. – Congeal