4

Let's say I have the following type:

type Device = {
  name: string;
  model: string;
}

type Service {
  name: string;
  device: Device;
}

Let's say I want a generic object Pair having a key and a func property. The key should be an existing field on the given generic type T and func (optional parameter) should be a function that receives a single parameter being the value received by indexing T with key. How do you actually type func properly?


interface Pair<T> {
  key: keyof T;
  func?: (value: ?) => ?
}

I tried having something like func: (value: T[key]) => typeof T[key] but I'm not sure what's the right approach.

Expected behaviour:

  • if I do p: Pair<Device> = { key: "name", func: (value) => ...}, value should be inferred as being a string
  • if I do p: Pair<Service> = { key: "device", func: (value) => ...}, value should be inferred as being a device

Extra:

What if I want to have a function that receives an array of these kind of pairs? How would the type of that parameter look like?

Adrian Pop
  • 1,879
  • 5
  • 28
  • 40

1 Answers1

2

You just need another generic parameter to "connect" the key and value types of the interface:

type Device = {
    name: string;
    model: number;
}

interface Pair<T, K extends keyof T> {
    key: K,
    func: (value: T) => T[K]
}

Might as well remove the function parameter:

interface Pair<T, K extends keyof T> {
    func: (value: T) => T[K]
}
const pair: Pair<Device, 'model'> = {
    // key: 'model',
    func: (value) => ''
}
Roberto Zvjerković
  • 9,657
  • 4
  • 26
  • 47
  • Hmm, I missed this, although it becomes a bit annoying having to type every pair. Initially I wanted to do something like `pairs = Pair[{ key: ..., func: ...}, { key: ..., func: ...}]` etc. I guess you can't do that easily with this. Is there any other way of declaring an array of Pairs while specifying the generics for each entry individually (I managed to do it using `as`, but it is ugly)? Maybe it helps knowing that func is optional (I updated the question), although I think that just (slightly) complicates things. – Adrian Pop Jun 15 '21 at 14:18
  • I believe it's actually possible to use mapped types to create a tuple of `key/func` objects from an interface, but I couldn't do it right now. This may help but it looks really complicated https://stackoverflow.com/questions/53058150/convert-an-interface-to-a-tuple-in-typescript – Roberto Zvjerković Jun 15 '21 at 17:23
  • That looks incredibly ugly, haha. I don't think I want to go there. – Adrian Pop Jun 15 '21 at 17:30
  • This one might be even uglier https://stackoverflow.com/questions/53503813/get-dictionary-object-keys-as-tuple-in-typescript – Roberto Zvjerković Jun 15 '21 at 17:31