4

That was a very long title to describe something quite simple.

interface Array<T> {
  sortBy<T, K>(keyFunction: (t: T) => K): T[];
}

.

export namespace util {
export function sortBy<Element, Key>(array: Element[], keyFunction: (t: Element) => Key ): Element[] {

    var arrayCopy = array.slice();

    var mapped = arrayCopy.map(function(el, i) {
      return { index: i, value: keyFunction(el) };
    });

    mapped.sort(function(a, b) {
      return +(a.value > b.value) || +(a.value === b.value) - 1;
    });

    for (let i = 0; i < array.length; i++) {
        let indexed = mapped[i];
        array[i] = arrayCopy[indexed.index];
    }

    var result = mapped.map(function(el){
      return arrayCopy[el.index];
    });

    return array;

}
}

.

Array.prototype.sortBy = function(keyFunction)  {return util.sortBy(this, keyFunction)}

Here I am implementing some sample code from MDN to sort by an array key instead of comparison function, and attempting to patch the Array prototype (comments on whether this is a good idea or not are expected) with it.

However, when I call it, Typescript can't verify the proper argument types of keyfunction, and thus a cast is required to prevent a compiler error:

it('should sort in-place', () => {
    var list = [{a: 'z', i: 2}, {a: 'x', i: 0}, {a: 'y', i:1}];
    // all good
    list.sortBy(<Function>(obj) => obj.i);
    expect(list.map((obj) => obj.a)
               .join('')
    ).toBe('xyz');
})


it('should sort in-place', () => {
    var list = [{a: 'z', i: 2}, {a: 'x', i: 0}, {a: 'y', i:1}];
    // Error TS2339: Property 'i' does not exist on type '{}'
    list.sortBy((obj) => obj.i);
    expect(list.map((obj) => obj.a)
               .join('')
    ).toBe('xyz');
})

Is there any way I can change this definition to work? Interestingly, calling util.sortBy(array, function) (i.e. as a function, not a method) has type parameters working correctly.

2 Answers 2

4

Your interface declaration's sort function is creating a new T (sortBy<T, K>) disconnected from the Array's T. Should be sortBy<K> and reuse the Array's T.

Correct Declaration

interface Array<T> {
  sortBy<K>(keyFunction: (t: T) => K): T[];
}

var list = [{a: 'z', i: 2}, {a: 'x', i: 0}, {a: 'y', i:1}];

// Okay
list.sortBy((obj) => obj.i);
Sign up to request clarification or add additional context in comments.

1 Comment

but were you able to implement sortBy with the T type parameter?
2

I just want to clarify how the implementation works. The type parameters on the implementation need not match the interface declaration, and can include the array type:

interface Array<T> {
   sortBy<K>(
      keyFunction: (t: T) => K
   ): T[];
}

Array.prototype.sortBy = function <T, K>(
   keyFunction: (t: T) => K
): T[] {
   ...
};

...

const array = [] as Something[];
const sorted = array.sortBy(x => x.SomeProperty);

Comments

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.