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:

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?
Object.setPrototypeOf(this, OrderedArray.prototype);looks extremely sketchy.Array. The error message ("length, a number, is not iterable/spreadable") is even fancier though than in the old question :-)@@speciesproperty on your class like this:static get [Symbol.species]() { return Array; }See this answer. I think this could be considered a dupe.