Define generic typescript sort function of a certain type


I have a bunch of objects that all have a property with the type of Moment but different names for the key.

I'm trying to write a small utility function using typescript where I can sort any of these objects. Could use some shoving in the right direction.

interface ExampleType1 {
    startDate: Moment;
    // ... other members
}

interface ExampleType2 {
    dueDate: Moment;
    // ... other members
}

interface ExampleType3 {
    createdAt: Moment;
}

I would expect my function look something like this (half pseudocode):

function sortByDateAsc(data: ATypeThatHasAMoment[], keyName: KeyToTheMomentTypeWithinATypeThatHasAMoment) {
    return data.sort((a, b) => {
        // whatever sort logic i use here
        // accessing the Moment object via a[keyName] and b[keyName]
    }
}

I've been fiddling with something like this

function sortByDateAsc<T, K extends keyof T>(data: T[], dateKey: K);

Though within the function T[K] doesn't have a type? I'm not sure how to tell the function "T[K] has to be of type Moment"



You could do it this way:

declare function sortByDateAsc<K extends keyof any, T extends Record<K, Moment>>(
  data: T[], 
  dateKey: K
): T[];

where you are constraining T to be a type where T[K] is Moment. This has the benefit of being simple to define and it works... but it might not give nice IntelliSense hints though. For example:

declare const ex1arr: ExampleType1[];
sortByDateAsc(ex1arr, "startdate"); // error
//            ~~~~~~ <-- "startdate" not in ExampleType1

This is given as an error on ex1arr, not an error on "startdate". Moreover it does not suggest "startDate" as a correction.


Instead you can use a more complicated conditional type like this:

type KeysMatching<T, V> = {[K in keyof T]: T[K] extends V ? K : never}[keyof T];
declare function sortByDateAsc<T extends any, K extends KeysMatching<T, Moment>>(
  data: T[], 
  dateKey: K
): T[];

Instead of constraining T, that signature constrains K to be the keys where T[K] matches Moment. Now the same example gives you a better error:

declare const ex1arr: ExampleType1[];
sortByDateAsc(ex1arr, "startdate"); // error
//                    ~~~~~~~~~~~ <-- does not match "startDate"

with better IntelliSense hints when you start typing the dateKey parameter (it suggests "startDate").


Either way should work. Hope that helps. Good luck!