woubuc
woubuc

Reputation: 927

Provide extendable Typescript interface in library

I'm building a library with Typescript that should support third-party plugins. The constructor function of the library should accept the options for the core library and for all plugins, since the module will load them in the constructor.

Is it possible to provide a base Options interface that each plugin can extend to add their own properties and types, so that throughout the top-level application (which uses the library and plugins), the Options interface reflects both the core options and those of the plugins?

So with a dependency structure like this:

- user-app
|- my-library
|- my-library-plugin-a
|- my-library-plugin-b

Where my-library defines the following:

export interface Options {
  coreOption : string;
}

And my-library-plugin-a extends it as follows:

export interface PluginAOptions extends Options {
  pluginA : boolean;
}

And my-library-plugin-b extends it as follows:

export interface PluginBOptions extends Options {
  pluginB : Record<string, string>;
}

To the developer of user-app, which has all three modules in its dependencies, the Options interface as exported from my-library should look like this:

interface Options {
  coreOption : string;
  pluginA : boolean;
  pluginB : Record<string, string>;
}

So that's what their autocomplete should show, and what the Typescript compiler should check for.

Is something like this at all possible with Typescript?

Upvotes: 2

Views: 990

Answers (2)

PStigerID
PStigerID

Reputation: 91

This is possible using typescript's declaration merging feature:

my-library:

export interface Options {
  coreOption : string;
}

my-library-plugin-a:

import {Options} from 'my-library';

export module 'my-library' {
    export interface Options {
      pluginA: boolean;
    }
}

my-library-plugin-b:

import {Options} from 'my-library';

export module 'my-library' {
    export interface Options {
      pluginB: Record<string, string>;
    }
}

Upvotes: 1

unional
unional

Reputation: 15589

You cannot expect to get the whole extended options from the my-library.

This is not about TypeScript, but about how dependency works. my-library knows nothing about the plugins and it supposed to. If it somehow can export options added by the plugins, it will be depends on those plugins, making the plugin architecture pointless.

In the user-app, you should simply use the options from plugin-a and plugin-b and combines them.

const theOption: PluginAOptions & PluginBOptions = { ... }

user-app is the one who knows and decided to use plugin-a and plugin-b. That's why it should and can use the types from those plugins.

Upvotes: 1

Related Questions