adammo
adammo

Reputation: 221

parameters where one is enum and the other oner one is union of string that are valid keys of this enum

I'm writing rather a simple sorting function for backend api. If there's no sorting it returns an empty string. If there's sorting it returns a value taken from an enum based on column + some minor modifications. However I have a trouble regarding types. sort when not null has, a sortBy which is T passed to this function (union of strings), this works ok. I do not know how do I pass Enum to the function where sort. sortBy is a valid key of that enum.

type SortState = null | {sortBy: string; sortAsc: boolean; }

const handleSort = <T, K extends T>(sort: SortState, sortEnum: K) => {
      if (!sort) {
        return '';
      }
      const name = sort.sortBy as T;
      const order = sort.sortAsc;
      const newQuery = `${order ? '-' : ''}${order ? sortEnum[name].replaceAll(',', ',-') : sortEnum[name]}`;

      return newQuery;
 };

enum SortEnum {
  bar = 'bar',
  foo = 'foo'
}

type TableHeaders = 'foo' | 'bar'

const sortQuery = handleSort<TableHeaders>({sortBy: 'bar', sortAsc: false}, SortEnum)

Upvotes: 2

Views: 36

Answers (1)

jcalz
jcalz

Reputation: 330216

Given your implementation and call, I'd be inclined to make handleSort() generic in the type K corresponding to the sortBy property of the sort argument, which is also a key of the sortEnum argument. Like this:

const handleSort = <K extends PropertyKey>(
    sort: null | { sortBy: K, sortAsc: boolean },
    sortEnum: Record<K, string>
) => {
    if (!sort) {
        return '';
    }
    const name = sort.sortBy;
    const order = sort.sortAsc;
    const newQuery = `${order ? '-' : ''}${
          order ? sortEnum[name].replaceAll(',', ',-') : sortEnum[name]
        }`;
    return newQuery;
};

The sortEnum parameter is of type Record<K, string> using the Record utility type to mean "a type with a string-valued property at every key of type K". The implementation type checks because the compiler understands that name is of type K (if sort is truthy) and therefore that sortEnum[name] is of type string.

Let's test the call side:

const sortQuery = handleSort(
    { sortBy: 'bar', sortAsc: false },
    SortEnum
); // okay

const sortQuery2 = handleSort(
    { sortBy: 'baz', sortAsc: false },
    SortEnum // error!
//  ~~~~~~~~ <-- Property 'baz' is missing in type 'typeof SortEnum'
);

Looks good. The compiler is happy when sortBy matches a key in SortEnum, and unhappy when it doesn't.

Playground link to code

Upvotes: 2

Related Questions