Misiu
Misiu

Reputation: 4919

Generic sort function with T[K] constraints

I'm learning TypeScript and currently, I'm trying to create a generic sort function.

I have two interfaces:

interface RefreshToken {
  client_icon?: string;
  client_id?: string;
  client_name?: string;
  created_at?: string;
  id?: string;
  is_current?: boolean;
  last_used_at?: string;
  last_used_ip?: string;
  type: "normal" | "long_lived_access_token";
}

interface PersistentNotification {
  created_at: string;
  message?: string;
  notification_id?: string;
  title: string;
  status?: "read" | "unread";
}

And my current function looks like so:

function SortByDateAscending<T, K extends keyof T>(values: T[], key: K) {
  const compare = (first: T, second: T) => {
    const timeA = first[key] ? new Date(first[key]) : 0;
    const timeB = second[key] ? new Date(second[key]) : 0;
    if (timeA < timeB) {
      return 1;
    }
    if (timeA > timeB) {
      return -1;
    }
    return 0;
  };
  return values.sort(compare);
}

the above code works, but I get warnings:

No overload matches this call. Overload 1 of 5, '(value: string | number | Date): Date', gave the following error. Argument of type 'T[K]' is not assignable to parameter of type 'string | number | Date'. Type 'T[keyof T]' is not assignable to type 'string | number | Date'. Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'string | number | Date'. Type 'T[string]' is not assignable to type 'string | number | Date'. Type 'T[string]' is not assignable to type 'Date'. Type 'T[keyof T]' is not assignable to type 'Date'. Type 'T[K]' is not assignable to type 'Date'.

Not sure if there is a way to add a constraint that will tell that T[K] must be string | number | Date

Here is my test project: https://stackblitz.com/edit/typescript-yzualr I currently passing only string types (one is optional), but will also use Date, so if T[K] is Date I shouldn't use new Date.

I'd like to avoid any changes to interfaces and of course creating separate functions per type.

Upvotes: 0

Views: 266

Answers (1)

A_A
A_A

Reputation: 1932

You could pass your sort method a method instead of a key name

function sortByDateAscending<T>(
   values: T[], getDate: (val: T) => string | number | Date) {
   // ...
}

tokens = sortByDateAscending(tokens, token => token.last_used_at);

Upvotes: 1

Related Questions