Daniel Rothig
Daniel Rothig

Reputation: 706

Typescript function definition: return nested object with keys looked-up from value array

I have a method that takes as an input an object whose values are arrays of strings; it returns an object whose values are objects with keys equal to the string values. E.g.

myFunction({foo: ['a', 'b']}) // ==> {foo: {a: Widget, b: Widget}}

I'm trying to define the signature of this function. My best attempt is:

declare function myFunction
  <Q extends string[], T extends {[key: string]: Q}>
  (keys: T) : {[key1 in keyof T]: {[key2 in T[key1][number]]: Widget}}

This almost works, except the key names of the nested objects are not inferred. The first example returns:

{
    foo: {
        [x: string]: Widget;
    };
}

whereas I want it to return

{
    foo: {
        a: Widget;
        b: Widget;
    };
}

Can that be done?

Upvotes: 0

Views: 571

Answers (2)

Maciej Sikora
Maciej Sikora

Reputation: 20132

In order to allow TS to fix the inference we need to add additionaly generic type for array keys members:

declare function myFunction
  <Q extends K[], T extends {[key: string]: Q}, K extends string>
  (keys: T): { [key1 in keyof T]: { [key2 in T[key1][number]]: Widget } }

Pay attention at Q extends K[], we now are saying that Q is not string[] but K[], it means TS will narrow members of array to more specific types then string.

Upvotes: 1

Limbo
Limbo

Reputation: 2290

If your array is always contains only "a" and "b", then you can define union type of string literals:

type MyUnionType = "a" | "b"

Then define variable foo as MyUnionType[]. The result is:

declare function myFunction
  <Q extends MyUnionType[], T extends {[key: string]: Q}>
  (keys: T) : {[key1 in keyof T]: {[key2 in MyUnionType]: Widget}}

Should work :)


But if your array is always consists of different strings, then it's impossible to implement such typing, because TypeScript is only design-time tool

Upvotes: 0

Related Questions