Vlad Miller
Vlad Miller

Reputation: 2280

How to dynamically infer return type from the argument object?

I am trying to build a strictly typed factory for my TS project and having issues figuring out if it is possible to automatically infer a schema from the passed argument.

type References = {
  [name: string]: any
}

function generateReferences<T extends string | number | symbol>(
  ref: References,
) {
  type Return = {
    [name in T]: string
  }

  return Object.keys(ref).reduce(
    (acc, name) => ({
      ...acc,
      [name]: `Hello from ${name}`,
    }),
    {},
  ) as Return
}

const cuteAnimals = generateReferences({
  rabbits: {},
  kittens: {},
})

console.log(cuteAnimals.rabbits)
console.log(cuteAnimals.kittens)
console.log(cuteAnimals.snakes) <!--- Should raise an error here

I am trying to achieve dynamic return type shaping based on the input object. Instead, TS threats return type as a simple Record.

A have found a workaround where I can define ref object as a separate variable and pass it's typeof as a template argument, but I'd prefer to have TS automatically infer the shape based on the input argument.

Would appreciate any ideas.

Upvotes: 2

Views: 313

Answers (1)

You should narrow References type instead of declaring it explicitly:

type Return<K, V> = Record<keyof K, V>

function generateReferences<References>(
  ref: References,
) {
  return Object.keys(ref).reduce(
    (acc, name) => ({
      ...acc,
      [name]: `Hello from ${name}`,
    }),
    {} as Return<References, string>,
  )
}

const cuteAnimals = generateReferences({
  rabbits: {},
  kittens: {},
})

console.log(cuteAnimals.rabbits)
console.log(cuteAnimals.kittens)
console.log(cuteAnimals.snakes) // error

Upvotes: 2

Related Questions