Arcnon
Arcnon

Reputation: 100

Returning object with key value pairs based on arguments in Typescript

I want to create a function that loops over key/value pairs in an object and that returns an object with the same keys, but each value has now become a function. I want to make sure that typescript correctly grasps the returned object for autocomplete. This works in de following silly function:

interface Args {
  [layer: string]: Boolean
}

function sillyFunction<T extends Args>(layers: T): T {
  return layers
}

sillyFunction({ layerName: true }).layerName // this works

But now I have the following code, where the layers object (key/boolean pairs) is used to create a new returnObject with key/function pairs.:

interface Args {
  [layer: string]: Boolean
}

interface ReturnedObject {
  [layer: string]: Function
}

function createFunctions<T extends Args>(layers: T): T {
  let returnObj = {} as ReturnedObject;

  for (let layer in layers) {
    returnObj[layer] = (label) => `${layer} ${label}`
  }

  return returnObj;
}

createFunctions({ layerName: true }).layerName // doesnt work

This obviously gives me the error 'ReturnedObject' is not assignable to type T. How can I make sure that typescript 'understands' that the keys inside the object are still the same, but values have become functions?

Upvotes: 1

Views: 2257

Answers (2)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250406

You need to use a mapped type to transform T to a new type with a the same properties, but a different type for each property. The predefined type Record will work well in this case, since all the keys will have the same type. Also I would be more specific then Function about the type of the function, Function is the any of function signatures, it allows invocation with any parameter

interface Args {
  [layer: string]: boolean
}

type ReturnedObject<T> =  Record<keyof T, (label: string) => string>

function createFunctions<T extends Args>(layers: T): ReturnedObject<T> {
  let returnObj = {} as ReturnedObject<T>;

  for (let layer in layers) {
    returnObj[layer] = (label) => `${layer} ${label}`
  }

  return returnObj;
}

var l = createFunctions({ layerName: true })
l.layerName("") 

Playground Link

Upvotes: 3

Juraj Kocan
Juraj Kocan

Reputation: 2878

your "createFunctions" return type is T but you expected to be ReturnedObject.

just define return type as ReturnedObject

function createFunctions<T extends Args>(layers: T): ReturnedObject

and it should work

Upvotes: -1

Related Questions