Reputation: 26467
Try out this link which includes following TS class that extends another class:
class ExtArray<T> extends Array<T> {
log() {
console.log(this)
}
}
var a = new ExtArray(1,2,3)
a.log()
a.log
should definitely exist, also, TS is able to compile that. However, the JS output fails to invoke ExtArray.prototype.log:
VM107:22 Uncaught TypeError: a.log is not a function
at <anonymous>:22:3
The output:
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var ExtArray = /** @class */ (function (_super) {
__extends(ExtArray, _super);
function ExtArray() {
return _super !== null && _super.apply(this, arguments) || this;
}
ExtArray.prototype.log = function () {
console.log(this);
};
return ExtArray;
}(Array));
var a = new ExtArray(1, 2, 3);
a.log();
What is wrong?
Upvotes: 1
Views: 281
Reputation: 46341
To start with, I strongly feel you've found a bug - please report it to typescript issues. (see jcalz's comment).
As for what's causing this, there's a technical explanation.
The Array
function is "special" in various ways, and one of these is that the function behaves identically whether you invoke it directly or as a constructor (link):
When Array is called as a function rather than as a constructor, it also creates and initializes a new Array object. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments.
Since TypeScript assumes that _super.apply(this, arguments)
will return this
but instead a new instance is returned, your a
variable actually holds a pure Array
instance, and not an ExtArray
.
By the way, that special behavior I mentioned about Array
is also true for other native objects, for example, RegExp
.
As for Array(...)
itself, it is meant to handle a super(...)
call correctly, but since it is invoked indirectly via apply
, it doesn't receive the correct new.target
and that doesn't work.
A potential solution to this by the compiler might be to call super(arguments)
or to reflect it with Reflect.construct(this, arguments)
.
Upvotes: 1
Reputation: 26467
I've found a working solution, basing on another SO thread solution. The trick is to add this line:
Object.setPrototypeOf(this, ExtArray.prototype);
to the constructor. All looks like this:
class ExtArray<T> extends Array<T> {
constructor(...args) {
super(...args)
Object.setPrototypeOf(this, ExtArray.prototype);
}
log() {
console.log(this)
}
}
var a = new ExtArray(1,2,3)
a.log()
Upvotes: 0