Reputation: 7098
In TypeScript, when I do:
class B { ... }
class A extends B { ... }
I can call methods/getters/setters of prototype A normally, without error.
However, when I do the same thing with native class:
class A extends Array { ... }
When I call methods/getters/setters of prototype A, it throws because properties in prototype A are all undefined.
Why?
Given this working code:
class Animal {
protected name: string;
protected age: number;
constructor(name: string, age: number){
this.name = name;
}
eat(){
console.log(`${this.name} has eaten.`);
}
sleep(){
console.log(`${this.name} has slept.`);
}
}
class Dog extends Animal {
constructor(...args: [string, number]){
super(...args);
}
bark(){
console.log(`${this.name} has barked.`);
}
}
const dog = new Dog('doggo', 2);
dog.eat();
dog.bark();
It will be transpiled into:
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
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 extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var Animal = /** @class */ (function () {
function Animal(name, age) {
this.name = name;
}
Animal.prototype.eat = function () {
console.log(this.name + " has eaten.");
};
Animal.prototype.sleep = function () {
console.log(this.name + " has slept.");
};
return Animal;
}());
var Dog = /** @class */ (function (_super) {
__extends(Dog, _super);
function Dog() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return _super.apply(this, args) || this;
}
Dog.prototype.bark = function () {
console.log(this.name + " has barked.");
};
return Dog;
}(Animal));
var dog = new Dog('doggo', 2);
dog.eat();
dog.bark();
So far, so good.
Now when I try to extend a native class (in this case, Array
):
class List<T> extends Array<T> {
private index: number;
constructor(...items: T[]) {
super(...items);
Object.defineProperty(this, 'index', {
value: 0,
writable: true,
configurable: true
});
}
get current() {
return this[this.index];
}
get max() {
return this.length - 1;
}
next() {
return this.index === this.max
? this.first()
: this[++this.index];
}
prev() {
return this.index === 0
? this.last()
: this[--this.index];
}
first() {
return this[this.index = 0];
}
last() {
return this[this.index = this.max];
}
}
const list = new List(1, 2, 3);
console.log('List:', list);
console.log('Length:', list.length);
console.log('Current value:', list.current);
console.log('Next value:', list.next());
It gets transpiled into this code that throws:
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
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 extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var List = /** @class */ (function (_super) {
__extends(List, _super);
function List() {
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
var _this = _super.apply(this, items) || this;
Object.defineProperty(_this, 'index', {
value: 0,
writable: true,
configurable: true
});
return _this;
}
Object.defineProperty(List.prototype, "current", {
get: function () {
return this[this.index];
},
enumerable: true,
configurable: true
});
Object.defineProperty(List.prototype, "max", {
get: function () {
return this.length - 1;
},
enumerable: true,
configurable: true
});
List.prototype.next = function () {
return this.index === this.max
? this.first()
: this[++this.index];
};
List.prototype.prev = function () {
return this.index === 0
? this.last()
: this[--this.index];
};
List.prototype.first = function () {
return this[this.index = 0];
};
List.prototype.last = function () {
return this[this.index = this.max];
};
return List;
}(Array));
var list = new List(1, 2, 3);
console.log('List:', list);
console.log('Length:', list.length);
console.log('Current value:', list.current);
console.log('Next value:', list.next());
Why?
Upvotes: 1
Views: 215
Reputation: 874
I see you are targeting ES5. When transpiling to ESNext your code will perfectly work as expected. If you need to target ES5 and extend native types (such as Array
) you will have to set in the constructor the prototype of your own class (Object.setPrototypeOf).
class List<T> extends Array<T> {
constructor(...items: T[]) {
super(...items);
// ...
Object.setPrototypeOf(this, Object.create(List.prototype)); // (Object as any).setPrototypeOf
}
// ...
}
Check the console in the playground example.
Check also another useful link on how to extend native types like Array
.
Upvotes: 2