Typescript: Declare type of a keyof a generic object param

I am trying to create a custom sort function for an array of objects of varying types. I would like the function to be generic enough to work for many types that contain atleast one string property.

interface Record {
  id: string
  name: string
}


const sortStr = (field: keyof Record, reverse: boolean) => {
  return (a: Record, b: Record) =>
    a[field] !== "" && b[field] !== ""
      ? (reverse ? -1 : 1) *
        a[field].localeCompare(b[field], undefined, { sensitivity: "base" })
      : a[field] !== "" && b[field] === ""
      ? -1
      : a[field] === "" && b[field] !== ""
      ? 1
      : 0
}

Can I declare the type of the ‘field’ param to be the keyof any object that is of type string?

For example, the function above won’t work if Record has a number property because localCompare is a string function.

interface Record {
  id: string
  name: string
  user: number
}

Solution

This worked for me, edited from caTS’ answer:

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

const sortStr = (field: KeysOfType<any, string>, reverse: boolean) => {
  return (a: any, b: any) => ...
}

>Solution :

You could define a type to get the keys that have a specific type like this:

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

and then you can use it in your function:

const sortStr = (field: KeysOfType<MyRecord, string>, reverse: boolean) => {

Note that I renamed Record to MyRecord since Record is already a built-in type.

Playground

Leave a Reply