简体   繁体   中英

TypeScript Array<T> inheritance

I have some experimental code that inherits from Array<T> ...

class SuperArray<T> extends Array<T> {
    constructor(...items) {
        super(...items);
    }

    public clear(): void {
        while (this.length > 0) {
            this.pop();
        }
    }
}

var array = new SuperArray("Matt", "Mark", "Luke", "John");

array.clear();

Playground

I've added the clear method just to illustrate the problem. When this is compiled and run in the browser, I get...

TypeError: array.clear is not a function

How is this completely valid code in TypeScript, but not valid in JavaScript, and is there a way to fix this?

BTW this is a breaking change in TS2.1

This TS documentation has a recommendation for a changing the prototype in your constructor

constructor(...items) {
    super(...items);
    Object.setPrototypeOf(this, SuperArray.prototype);
}

Your code compiles into this when targeting es5 :

var SuperArray = (function (_super) {
    __extends(SuperArray, _super);
    function SuperArray() {
        var items = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            items[_i] = arguments[_i];
        }
        return _super.apply(this, items) || this;
    }
    SuperArray.prototype.clear = function () {
        while (this.length > 0) {
            this.pop();
        }
    };
    return SuperArray;
}(Array));
var array = new SuperArray("Matt", "Mark", "Luke", "John");
array.clear();

It's not possible to extend native objects like that, which is why you're getting this error.
If you'll target es6 then the compiled js will look like this:

class SuperArray extends Array {
    constructor(...items) {
        super(...items);
    }
    clear() {
        while (this.length > 0) {
            this.pop();
        }
    }
}
var array = new SuperArray("Matt", "Mark", "Luke", "John");
array.clear();

And that will work just fine (if you run it in a browser that supports es6 classes).


Edit

I'll just paste parts of the article Subclassing builtins in ECMAScript 6 which explains why it can't be done with es5 :

Allocation obstacle: MyArray allocates the wrong kind of object
Array instances are special – the ECMAScript 6 specification calls them exotic. Their handling of the property length can't be replicated via normal JavaScript. If you invoke the constructor MyArray then an instance of MyArray is created, not an exotic object.

Initialization obstacle: MyArray can't use Array for initialization
It is impossible to hand an existing object to Array via this – it completely ignores its this and always creates a new instance.

Since Typescript 2.2 you can access new.target from the constructor.

Thus you can make the following call after having called super() :

Object.setPrototypeOf(this, new.target.prototype);

It should solve the propotype chain issue

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM