mnasyrov / ditox

Dependency injection for modular web applications

Home Page:https://ditox.js.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Optional exports in ModuleDeclaration

epodivilov opened this issue · comments

Hi. I would like to clarify that in the ModuleDeclaration type the field exports and its keys are declared as optional - is this the intention or is it a mistake?

I have a question during the implementation of the API module. For example:

export type ApiType = {
  methodOne: () => void;
  methodTwo: () => void;
};

export const TOKEN_ONE = token<ApiType["methodOne"]>();
export const TOKEN_TWO = token<ApiType["methodTwo"]>();

export default declareModule<ApiType>({
  factory() {
    return {
      methodOne: () => {},
      methodTwo: () => {},
    };
  },
  exports: {
    methodOne: TOKEN_ONE
  },
});

In what follows, I use the ApiType type. The type tells us that there are two methods and we have two tokens to get the methods from the DI. But when we try to get the second method there will be an error, because although the module returns the second method, the token for it is not exported.

And the reverse situation is handled correctly: if you try to return less methods from the factory, TS will show an error.

What is the best way to handle such a problem? Or maybe you can suggest another way to work with modules?

Hello! The optional exports keys are done on purpose. You can export the whole module by its own token and simultaneously choose to export some properties of the module using their own token.

I would suggest to export a service object or the whole module itself. Below are examples:

export type ApiType = {
  methodOne: () => void;
  methodTwo: () => void;
};

export const API_TOKEN = token<ApiType>();

export const TOKEN_ONE = token<ApiType["methodOne"]>();

// Export the whole module
export const API_MODULE1 = declareModule<Module<ApiType>>({
  token: API_TOKEN,
  factory: () => {
    return {
      methodOne: () => {},
      methodTwo: () => {},
    }
  },

  // Optionally a property can be exported using its token
  exports: {
    methodOne: TOKEN_ONE
  }
});

// Export a service object
export const API_MODULE2 = declareModule({
  factory: () => {
    return {
      service: {
        methodOne: () => {},
        methodTwo: () => {},
      }
    }
  },
  exports: {
    service: API_TOKEN
  }
});

Personally, I prefer to explicitly export a service object. Usually the module exports different types of things, which are more convenient to separate by different types and tokens.

Thanks for the clarification.

In other words, if I need to get a module that provides several mandatory methods, there are two options:

  • make several modules each with a unique token
  • make one module with a token and refer to the module everywhere and in it refer to the methods, not directly to the methods

Do I understand the concept correctly?

@epodivilov

While there is a possibility to provide the each value by its own module, values can be created by a module factory and be exported the values by they own tokens (bound to a container):

export const API_MODULE = declareModule({
  factory: () => {
    return {
      methodOne: () => {},
      methodTwo: () => {},
    }
  },
  exports: {
    methodOne: TOKEN_ONE,
    methodTwo: TOKEN_TWO,
  }
});

make one module with a token and refer to the module everywhere and in it refer to the methods, not directly to the methods

Yes, it can be used too:

export const API_MODULE = declareModule({
  token: API_TOKEN,
  factory: () => {
    return {
      methodOne: () => {},
      methodTwo: () => {},
    }
  }
});

There are different strategies how to create modules and export values, it depends on your cases and conditions.