Home > Net >  Advanced Typescript inference
Advanced Typescript inference

Time:06-01

I am currently trying to build a plugin library. These plugins have some methods in common, and others that are really different.

Example :

class PluginA extends Plugin {
  specificAMethod() {
    ....
  }
}

class PluginB extends Plugin {
  specificBMethod() {
    ....
  }
}

What I want to do, is have a plugin registry that goes something like this :

const pluginRegistry: Record<string, Plugin> = {
 a: PluginA,
 b: PluginB
}

The problem with this, is that if I try to do a.specificAMethod() I will get the error method specificAMethod does not exist on type Plugin.

And since this is a lib, I want it to be automatic, so that when the users imports a Plugin, it automatically has the right type instead of doing (a as PluginA).specificAMethod().

I also tried union types instead of inheritance, but same problem.

I also know that I can get away with it with just doing :

const pluginRegistry = {
  a: PluginA,
  b: PluginB
}

But I don't like it since it works when exposing the library but I actually want to have a type that I can reuse because I need it for other abstract cases and it is also more interesting this way.

Is there a way that I can get TypeScript to correctly infer the type of each plugin automatically ?

CodePudding user response:

I also know that i can get away with it with just doing :

const pluginRegistry = {
    a: PluginA,
    b: PluginB
}

But i don't like it since it works when exposing the library but i actually want to have a type that i can reuse because i need it for other abstract cases...

That's not a problem, just get the type from the object:

const pluginRegistry = {
    a: PluginA,
    b: PluginB
} as const;
export type PluginRegistry = typeof pluginRegistry;
//          ^? −−−− type is { readonly a: typeof PluginA, readonly b: typeof PluginB };

Playground link

...and it is also more interesting this way

If you don't want to do that, you'll have to write your type with explicit keys and types for them:

export type PluginRegistry = {
    // `readonly` is optional, depends on your use case
    readonly a: typeof PluginA;
    readonly b: typeof PluginB;
};

const pluginRegistry: PluginRegistry = {
    a: PluginA,
    b: PluginB,
};

...since Record<string, Plugin> will be quite broad.

Playground link

  • Related