'TypeScript Mapped Type - Convert type properties to instances of type

I am trying to convert an object full of class constructors to a mapped object with properties that are functions that return an instance.

For example:

// From an external auto-generated utility:
class FooClient { getData() {}}
class BarClient { getData() {}}

export const Clients = {
    FooClient,
    BarClient
}

Which can be consumed:

import { Clients } from './somemodule';

type ClientTypes = typeof Clients;

function getFooData(c: ClientTypes) {
  const fooClient = new c.FooClient();
  return fooClient.getData();
}

What I am shooting for is to replace the classes / constructors with factory functions:

type Factories = {
  [Property in keyof ClientTypes as `use${Property}`]: () => ClientTypes[Property]
}

function getFooData(f: Factories) {
  const fooClient = new (f.useFooClient());  // <-- use function returns the original constructor type
  return fooClient.getData();
}

My question is, how can I change the return type on the mapped type such that it does not return a constructor, but rather, I want it to return the value as if the constructor had been called.

For example:

function getFooData(f: Factories) {
  const fooClient = f.useFooClient();  // <-- returns an instance instead
  return fooClient.getData();
}

Is there some kind of keyword that can indicate that I want my function to return an instance of a type instead of the type itself?



Solution 1:[1]

You were close, however, when you were typing the return type of use_ you returned the class type. However, you need to do a little bit more work to make it act as a regular function that returns a class instead of a constructor. You can do this by creating a new function that returns InstanceType<ClientTypes[Property]>.

class FooClient { getData() {}}
class BarClient { getData() {}}

export const Clients = {
    FooClient,
    BarClient
}

type ClientTypes = typeof Clients;

type Factories = {
  [Property in keyof ClientTypes as `use${Property}`]: () => InstanceType<ClientTypes[Property]>
}


function getFooData(f: Factories) {
  const fooClient = f.useFooClient();
  return fooClient.getData();
}

TypeScript Playground Link

If you wanted to include constructor parameters in this callback, then add a rest parameter to the function type with the type of ConstructorParameters<ClientTypes[Property]>.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 nicholas