Assume we have:
class FinalClass {
...
}
How to modify it to make
class WrongClass extends FinalClass {
...
}
or
new WrongClass(...)
to generate an exception? Perhaps the most obvious solution is to do the following in the FinalClass's constructor:
if (this.constructor !== FinalClass) {
throw new Error('Subclassing is not allowed');
}
Does anyone have a more cleaner solution instead of repeating these lines in each class that supposed to be final (probably with a decorator)?
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.
constructor.name
is easy enough to spoof. Just make the subclass the same name as the superclass:
class FinalClass {
constructor () {
if (this.constructor.name !== 'FinalClass') {
throw new Error('Subclassing is not allowed')
}
console.log('Hooray!')
}
}
const OopsClass = FinalClass
;(function () {
class FinalClass extends OopsClass {}
const WrongClass = FinalClass
new OopsClass //=> Hooray!
new WrongClass //=> Hooray!
}())
Better to check the constructor
itself:
class FinalClass {
constructor () {
if (this.constructor !== FinalClass) {
throw new Error('Subclassing is not allowed')
}
console.log('Hooray!')
}
}
const OopsClass = FinalClass
;(function () {
class FinalClass extends OopsClass {}
const WrongClass = FinalClass
new OopsClass //=> Hooray!
new WrongClass //=> Uncaught Error: Subclassing is not allowed
}())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With