Home > Blockchain >  How do I enforce a key of an object to be of a certain type?
How do I enforce a key of an object to be of a certain type?

Time:03-30

I'm trying to make a function that can take a generic objects key and use that to create a function to sort string properties using String.localCompare but I can't get TypeScript to understand that a[key] and b[key] are supposed to be strings.

How do I enforce this in TS?

const makeStringSort = <T>(key: keyof T) => {
  return (a: T, b: T) => {
    return a[key].toLowerCase().localeCompare(b[key].toLowerCase());
  };
};
Property 'toLowerCase' does not exist on type 'T[keyof T]'

// example usage

interface Item {
  prop: string;
  other: number;
}
const sortFn = makeStringSort<Item>("prop");
const items: Item[] = [];
items.sort(sortFn);

CodePudding user response:

Updated answer: use mapped types

const makeStringSort = <T extends string>(key: T) => {
  return <
    U extends {
      [Property in T]: string;
    }
  >(
    a: U,
    b: U
  ) => {
    return a[key].toLowerCase().localeCompare(b[key].toLowerCase());
  };
};

CodePudding user response:

Reading your code seems that key is a subtype of string and a and b are Record<string, string>.

const makeStringSort = <T extends Record<string, string>>(key: keyof T) => {
  return (a: T, b: T) => {
    return a[key].toLowerCase().localeCompare(b[key].toLowerCase());
  };
};

CodePudding user response:

If you define a helper:

type KeysOfType<O, T> = {
  [K in keyof O]: O[K] extends T ? K : never;
}[keyof O];

to extract the keys of O that have a value of type T, then you can use it to constrain your function:

const makeStringSort = <T>(key: KeysOfType<T, string>) => {
  return <TV extends { [K in KeysOfType<T, string>]: string }>(a: TV, b: TV) => {
    return a[key].toLowerCase().localeCompare(b[key].toLowerCase());
  };
};

Playground Link

  • Related