0

I created a new class called OrderedArray which extends a system Array:

export class OrderedArray<T> extends Array<T> {
    /* istanbul ignore next */
    constructor(elements: T[], private _comparer: (a: T, b: T) => number) {
        super(...elements);
        Object.setPrototypeOf(this, OrderedArray.prototype);
        this.sort(this._comparer);
    }

    /**
     * Performs a subsequent ordering of the elements in a sequence in ascending order according to a key.
     * @override
     */
    public thenBy(keySelector: (key: T) => any): OrderedArray<T> {
        return new OrderedArray(
            this,
            composeComparers(this._comparer, keyComparer(keySelector, false))
        );
    }

    /**
     * Performs a subsequent ordering of the elements in a sequence in descending order, according to a key.
     * @override
     */
    public thenByDescending(keySelector: (key: T) => any): OrderedArray<T> {
        return new OrderedArray(
            this,
            composeComparers(this._comparer, keyComparer(keySelector, true))
        );
    }

    /**
     * Converts the OrderedList back to an array to be able to chain other actions to.
     */
    public toArray(): T[] {
        let returnArray = new Array();
        returnArray.addRange(this);
        return returnArray;
    }
}

Which transpiles down to ES5:

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);
};

function __extends(d, b) {
    extendStatics(d, b);
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}

var OrderedArray = /** @class */ (function (_super) {
    __extends(OrderedArray, _super);
    /* istanbul ignore next */
    function OrderedArray(elements, _comparer) {
        var _this = _super.apply(this, elements) || this;
        _this._comparer = _comparer;
        Object.setPrototypeOf(_this, OrderedArray.prototype);
        _this.sort(_this._comparer);
        return _this;
    }
    /**
     * Performs a subsequent ordering of the elements in a sequence in ascending order according to a key.
     * @override
     */
    OrderedArray.prototype.thenBy = function (keySelector) {
        return new OrderedArray(this, composeComparers(this._comparer, keyComparer(keySelector, false)));
    };
    /**
     * Performs a subsequent ordering of the elements in a sequence in descending order, according to a key.
     * @override
     */
    OrderedArray.prototype.thenByDescending = function (keySelector) {
        return new OrderedArray(this, composeComparers(this._comparer, keyComparer(keySelector, true)));
    };
    /**
     * Converts the OrderedList back to an array to be able to chain other actions to.
     */
    OrderedArray.prototype.toArray = function () {
        var returnArray = new Array();
        returnArray.addRange(this);
        return returnArray;
    };
    return OrderedArray;
}(Array));

This allow me to do: array.orderBy(m => m.field).thenBy(m => m.field2);

This will return an OrderedArray object, which in the console looks like: console output

Which looks like an array, even prototypes as such, but when calling native array functions like slice, the following error occurs: Uncaught TypeError: CreateListFromArrayLike called on non-object

Now I have to call toArray() after the last orderBy function call to convert it to a proper array, but is this necessary? Is there a way to call native array functions on my object?

5
  • Given this is typescript, please show us the code that it was transpiled to (and possibly the typescript config). It should work in native ES6, it should not work in other environments. Object.setPrototypeOf(this, OrderedArray.prototype); looks extremely sketchy. Commented Dec 16, 2019 at 22:06
  • Oh, actually it shouldn't work in ES6 either. You don't support the overwritten constructor signature, which is a must when subclassing Array. The error message ("length, a number, is not iterable/spreadable") is even fancier though than in the old question :-) Commented Dec 16, 2019 at 22:07
  • 1
    You can define the @@species property on your class like this: static get [Symbol.species]() { return Array; } See this answer. I think this could be considered a dupe. Commented Dec 16, 2019 at 22:10
  • @Bergi what do you mean it's not supposed to work? I'm using it in production now and I haven't had any issues, I added the ES5 transpiled code to my post. Commented Dec 17, 2019 at 14:35
  • @RogerFar I mean that calling native array methods (that instantiate another array) is not supposed to work in ES6 given how you've written your constructor. Commented Dec 17, 2019 at 21:27

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.