[SOLVED] How to infer type from an Array of Interface


I define a Manager to manage a set of modules, and I want to access the method of the Module instance through the Manager instance. Can this be inferred?

interface Module<T extends string> {
  readonly moduleName: T;

class Manager<
  T extends Module<any>[],
> {
  api: any;

  constructor(modules: T) {}

class AMoudle {
  static moduleName = 'a'
  aFun() {}

class BMoudle {
  static moduleName = 'b'
  bFun() {}

const manager = new Manager([AMoudle, BMoudle]);




Yes we can! First we get an array of module names:

type GetModNames<Mods, Names extends string[] = []> = Mods extends readonly [Module<infer Name>, ...infer Rest] ? GetModNames<Rest, [...Names, Name]> : Names;

This "loops" through the modules and infers each of the names, then adds the name to the result. Finally when it is done, it "returns" the names it inferred.

This requires some changes beforehand:

Module names have to be readonly as well:

class AMoudle {
  static readonly moduleName = 'a'
  aFun() {}

The Manager class should expect readonly arrays:

class Manager<
  T extends ReadonlyArray<Module<string>>,
> {

And when you make the manager you have to use as const:

const manager = new Manager([AMoudle, BMoudle] as const);

With that in place, here’s how we will make the api property’s type:

type MakeApi<Mods> = {
  //@ts-ignore ¯\_(ツ)_/¯
  [K in GetModNames<Mods>[number]]: InstanceType<Exclude<Mods[number], Exclude<Mods[number], { moduleName: K }>>>

We go through each of the mod names we got, and get the module with this interesting construction:

Exclude<Mods[number], Exclude<Mods[number], { moduleName: K }>>

Then because the module is a class we need an instance of it, so we wrap that in InstanceType.

Unfortunately I don’t know a method to get around the error, so I used ts-ignore there 🙁


Answered By – kellys

Answer Checked By – David Marino (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *