Home > Mobile >  Type for all keys which would give numeric values
Type for all keys which would give numeric values

Time:07-18

Let's say I want to write a sortBy function, that takes a list of Ts and a key of T to sort the list by.

To properly work I want the key to only accept keys of T that are numeric.

I have this, but I don't know how to restrict Key so that T[Key] refers to a number:

const sortBy = <T, Key extends keyof T>(items: T[], key: Key) { 
  // impl
}

I played around with this, but could not get it to work:

type NumericAttributesOf<T> = {
  [K in keyof T]: T[K] extends number ? T[K] : never
}

CodePudding user response:

It seems to be working for me, I think you forgot the arrow => in your sortBy function.

type NumericAttributesOf<T> = {
  [K in keyof T]: T[K] extends number ? T[K] : never
}

//                                                                           ||
//                                                                           VV
const sortBy = <T, Key extends NumericAttributesOf<T>>(items: T[], key: Key) => { 
  // impl
}

sortBy([{
  name: 'Test',
  age: 20
}], 'name') // ERROR

sortBy([{
  name: 'Test',
  age: 20
}], 'age') // Ok

CodePudding user response:

There are two issues with

type NumericAttributesOf<T> = {
  [K in keyof T]: T[K] extends number ? T[K] : never
}
  1. T[K] extends number ? T[K] : never selects value type while you're looking for key type, so it should be [K in keyof T]: T[K] extends number ? K : never
  2. The result of mapped type is an "object", you need to use lookup to extract union of allowed keys.

Here's a working example:

type NumericKeys<T> = { 
  [K in keyof T]: T[K] extends number ? K : never 
}[keyof T]

const sortBy = <T, Key extends NumericKeys<T>>(items: T[], key: Key) => { 
  // impl
}

sortBy([{ str: 'str', num: 1}], 'str') // Error: Argument of type '"str"' is not assignable to parameter of type '"num"'
sortBy([{ str: 'str', num: 1}], 'num') // OK

Playground

  • Related