Typescript: Is it possible to force 'keyof T' to be a string?

Viewed 165

I have a function typed as follows:

const getByPrimaryKey = <E>(list: E[], primaryKey: keyof E): E => {...} 

Can I add a type validation to the value associated to primaryKey ? For exemple:

interface User {
  id: string
  name?: string
  age: number
}

const users: User[] = [{id: '1', age: 1}]

getByPrimaryKey<User>(users, 'id') // OK since id is a string
getByPrimaryKey<User>(users, 'name') // NOT OK because name maybe undefined
getByPrimaryKey<User>(users, 'age') // NOT OK because age is a number


1 Answers

You need to combine a couple of conditional types.

To get all keys that are non-optional:

type RequiredKeys<T> = { [K in keyof T]-?: ({} extends { [P in K]: T[K] } ? never : K) }[keyof T];

Get all keys that have a specific type of the value:

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

Then you can use the intersection of both:

type NonOptionalTypeMatchingKeys<T, V> = TypeMatchingKeys<T, V> & keyof Pick<T, RequiredKeys<T>>;
const getByPrimaryKey = <E>(list: E[], primaryKey: NonOptionalTypeMatchingKeys<E, string>): E => {
    ...
}
Related