2

If I have this interface:

interface Interface {
  a: number
  b: number
  c: string
  d: string
  e: number[]
}

How would I get the keys of Interface where the type of the value in Interface is/extends a certain type? It's like Pick<T, K> but instead of returning the keys that match a type it returns the keys whose values match a type.

I'm looking for something like this:

type KeyOfWhereValueMatches<T, U extends U[keyof U]> = // ???
KeyOfWhereValueMatches<Interface, number> // 'a' | 'b'
KeyOfWhereValueMatches<Interface, string> // 'c' | 'd'
KeyOfWhereValueMatches<Interface, number[]> // 'e'
Lauren Yim
  • 12,700
  • 2
  • 32
  • 59

1 Answers1

2

Something like this should do it:

type KeyOfWhereValueMatches<
    T,
    U,
    S = { [key in keyof T]: U extends T[key] ? key : never; }
    > = S[keyof S]

How it works: T is your interface, U is the type you want to get keys for.

S is a calculated type that runs through each key of the interface type. If U is assignable to the type of that key (i.e. number extends Interface[b]) then the new type of that field is a string literal of its key. If not, it's a never.

For KeyOfWhereValueMatches<Interface, number>, S will be equal to:

interface S {
    a: "a";
    b: "b";
    c: never;
    d: never;
    e: never;
}

Lastly, S[keyof S] uses lookup types to return the types for all the available keys in S. These will be either the string literals of the suitable keys, or never. Since never isn't a thing that happens, the lookup just returns the string literals.

ben
  • 361
  • 2
  • 13