Search code examples
typescriptinterfacestatic-typing

Plugin-augmented interface


I am working on a "manager" class which allows/enables "plugins". Each plugin will (optionally) augment a data member of the manager class.

// manager.ts
interface Data {
    // some props
}

class Manager {
    data: Data;
    // TSC Problem: "Property 'data' has no initializer and is not definitely assigned in the constructor."

    constructor() {
        // can not set all possible properties of this.data because
        // the Manager class doesn't know what all will be plugged in
    }
}

Plugins get a reference to the class instance they are plugging into. They may also augment the interface of Data to better define the data object?

// plugins/A.ts
interface Data {
    // additional props
}

function A(boss: Manager) {
    // "A" adds data to boss.data that is unique to "A"
}

These are my questions I think:

  1. Is this a good pattern?
  2. Is this the way to accomplish this, or is there a better way?

I read about Declaration Merging (https://www.typescriptlang.org/docs/handbook/declaration-merging.html) and that sounds like the best option in my case because there will be many Plugins that are all independant and Discriminated Unions (https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types) would likely be out of the question pretty immediately.

EDIT: Maybe my question isn't clear; Is it possible to use declaration merging across multiple files?


Solution

  • I just figured this out.

    // Manager.ts
    interface Data {
        // some props maybe
    }
    
    class Manager {
        data: Data;
        // TSC Problem: "Property 'data' has no initializer and is not definitely assigned in the constructor."
    
        constructor() {
            // can not set all possible properties of this.data because
            // the Manager class doesn't know what all will be plugged in
        }
    }
    
    
    // plugins/A.ts
    declare module "../Manager" {
      interface Data {
        Scale: number;
      }
    }
    
    function A(boss: Manager) {
        // "A" adds data to boss.data that is unique to "A"
    }