Using ES6 class
syntax, I'm wondering why the instanceof
operator doesn't work for the inheritance chain when there are more than one chain of inheritance?
(optional read)How
instanceof
operator works?In
obj instanceof Constructor
, theinstanceof
operator checks if the'prototype'
property of theConstructor
function is present in the prototype chain of theobj
. If it is present, returntrue
. Otherwise,false
.
In the following snippet, the BTError
inherits from Error
(1.) and SomeError
extends from BTError
(3.).
But as we can see from (4.), the instanceof
operator results false
for new SomeError() instanceof BTError
which in my understanding should be true
.
class BTError extends Error {}
console.log('1.', Reflect.getPrototypeOf(BTError.prototype) === Error.prototype); // 1. true
console.log('2.', new BTError() instanceof Error); // 2. true
console.log('');
class SomeError extends BTError {}
console.log('3.', Reflect.getPrototypeOf(SomeError.prototype) === BTError.prototype); // 3. true
console.log('4.', new SomeError() instanceof BTError); // 4. false
console.log('');
class SpecificError extends SomeError {}
console.log('5.', Reflect.getPrototypeOf(SpecificError.prototype) === SomeError.prototype); // 5. true
console.log('6.', new SpecificError() instanceof Error); // 6. true
console.log('7.', new SpecificError() instanceof BTError); // 7. false
console.log('8.', new SpecificError() instanceof SomeError); // 8. false
Is there anything non-trivial which I'm unable to comprehend or is instanceof
operator just acting weird?
Focusing on the last piece of your example
You're converting this code using BabelJS to make it compatible
class BTError extends Error {}
class SomeError extends BTError {}
class SpecificError extends SomeError {}
console.log('6.', new SpecificError() instanceof Error);
console.log('7.', new SpecificError() instanceof BTError);
console.log('8.', new SpecificError() instanceof SomeError);
This is the transpiled version of the code above
'use strict';
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
var BTError = function(_Error) {
_inherits(BTError, _Error);
function BTError() {
_classCallCheck(this, BTError);
return _possibleConstructorReturn(this, (BTError.__proto__ || Object.getPrototypeOf(BTError)).apply(this, arguments));
}
return BTError;
}(Error);
var SomeError = function(_BTError) {
_inherits(SomeError, _BTError);
function SomeError() {
_classCallCheck(this, SomeError);
return _possibleConstructorReturn(this, (SomeError.__proto__ || Object.getPrototypeOf(SomeError)).apply(this, arguments));
}
return SomeError;
}(BTError);
var SpecificError = function(_SomeError) {
_inherits(SpecificError, _SomeError);
function SpecificError() {
_classCallCheck(this, SpecificError);
return _possibleConstructorReturn(this, (SpecificError.__proto__ || Object.getPrototypeOf(SpecificError)).apply(this, arguments));
}
return SpecificError;
}(SomeError);
console.log('6.', new SpecificError() instanceof Error); // 6. true
console.log('7.', new SpecificError() instanceof BTError); // 7. false
console.log('8.', new SpecificError() instanceof SomeError); // 8. false
I think the problem stems from the _inherit
method, that assigns to subClass.prototype
not the superClass.prototype
directly, but an object created by merging that and another set of default properties.
With this chain of prototypes the inheritance would work but the instanceof operator would fail to travel it by reference, and so you get false
where you expect true
.
Apparently, according to this bug report, it's a known and expected behaviour (ie. limitation) and a possible workaround is to use babel-plugin-transform-builtin-extend
Had this problem with TypeScript. Solved it by adding the following in the class constructor after the super calls:
Object.setPrototypeOf(this, YOUR_CLASS_HERE.prototype);
Not sure if it helps you.
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