3

I am trying to do something like so, where I want to have a an idKey which is a keyof T but only the properties of type number.

Is this possible?

export const updateList = <T>(
  idKey: keyof T extends number,
  list: T[],
  id: number
) => {
  const listItem = list.find(x => x[idKey] === id)
}

Having extends number is not right as gives an error, leaving it off also gives an error of:

This condition will always return 'false' since the types 'T[keyof T]' and 'number' have no overlap.

2
  • I'm fairly certain you can't constrain the keys to those that have number values, but by using a record of number values for the list elements, you can kind of achieve what you want: tsplay.dev/m0AYqW. I can turn it into an answer if this works for you. Commented Aug 26, 2022 at 22:47
  • Hi, yes I think this works for that I am after. If you could create this into an answer and then give and details about what PropertyKey and Record are that would be amazing thank you Commented Aug 27, 2022 at 18:39

2 Answers 2

3

The type system isn't strong enough to constrain idKey to keys for which T has a number value, but you can get around this by instead using a generic parameter K for the type of idKey and constraining the list elements to objects that have a number value for that key K.

const updateList = <K extends PropertyKey>(
  idKey: K,
  list: Record<K, number>[],
  id: number
) => {
  const listItem = list.find(x => x[idKey] === id)
  // ..
}

The builtin type PropertyKey is equal to string | number | symbol (i.e. any valid property key type), and Record<K, number> (see docs) stands for {[P in K]: number}. Basically, the type signature enforces that the list parameter is assignable to an array of singleton objects with an idKey number property, which also allows passing an array of objects with additional properties, as long as idKey has a number value.

Calls to updateList will now only type check correctly:

const list = [{a: 8, b: 'x'}]

updateList('a', list, 0)

updateList('b', list, 0) // Error

Note that since the key parameter is leading the type inference, the error will be on the list parameter rather than on 'b', but that shouldn't be a problem.

TypeScript playground

Sign up to request clarification or add additional context in comments.

Comments

1

If I understood your problem correctly, it is not the idKey itself that needs to be of type number, but rather the type T[keyof T] needs to extend type number. So what you want to do is to restrict the T type to types that have values of type number. Something like this should work.

export const updateList = <T extends { [key: PropertyKey]: number }>(
  idKey: keyof T,
  list: T[],
  id: number
) => {
  const listItem = list.find(x => x[idKey] === id)
}

Playground

Update: Oblosys answer is more flexible.

1 Comment

Thank you so much. This is good for what I need in regards to not showing an error. However the intellisense still shows all fields regardless of if they are number fields or not. Do you know if there is a way around this?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.