1

In typescript it's possbile to declare a function that expects a type and one of its keys as parameters:

function foo<T, K extends keyof T>(object: T, key: K)

In the function body you're then able to get or set the value of the object's key value and even extract the type of the key (T[k]):

function setter<T, K extends keyof T>(object: T, key: K){
  return (value: T[K]) => {
    obj[key] = value;
  };      
}

function logger<T, K extends keyof T>(object: T, key: K){
  console.log(obj[key]);   
}

Question:

Is it possbile to have generic type K that extends keyof T and is an array? (In other words: K must be an array and a key of type T)

Example use case:

function lengthLogger<T, K /*extends keyof T and is array*/>(object: T, key: K){
  console.log(object[key].length)     
}
ysfaran
  • 5,189
  • 3
  • 21
  • 51

1 Answers1

2

There are several ways to do this. The best one is to declare T as being a record of K of any[]

function lengthLogger<T extends Record<K, any[]>, K extends keyof T>(object: T, key: K) {
    console.log(object[key].length)
}

lengthLogger({
    foo: "",
    bar: ["", ""]
}, 'bar')

lengthLogger({
    foo: "",
    bar: ["", ""]
}, 'foo') // Error

This gives both call site and declaration type safety.

Another option you will find is to use conditional types to extract in K only the properties of a specific type. This works well for the call site but you will need type assertions for the declaration:

type ExtractKeysOfType<T, TValue> = { [P in keyof T]: T[P] extends TValue ? P : TValue}[keyof T]
function lengthLogger<T , K extends ExtractKeysOfType<T, any[]>>(object: T, key: K) {
    console.log((object as any)[key].length)
}

lengthLogger({
    foo: "",
    bar: ["", ""]
}, 'bar')

lengthLogger({
    foo: "",
    bar: ["", ""]
}, 'foo') // Error
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357