Utilizing Typescript to inject dependencies and receive arguments within a class

I am currently using InversifyJS with the container.

My goal is to inject the templateEngine and provide args (such as host, port, etc...) in the constructor.

const container = new Container();
container.bind<MailerInterface>(TYPES.Mailer).to(NodeMailer);

This is what my Mailer class looks like:

import * as nodemailer from "nodemailer";
import {TemplateEngineInterface} from "../../Domain/TemplateEngine/TemplateEngineInterface";
import TYPES from "../../../../config/inversify.types";
import {inject, injectable, named} from "inversify";

@injectable()
export class NodeMailer implements MailerInterface {

    private transporter: any;
    private templateEngine: TemplateEngineInterface;

    constructor(
        @inject(TYPES.TemplateEngine) templateEngine: TemplateEngineInterface,
        host: string,
        port: number,
        secure: boolean,
        username: string,
        password: string
    ) {

        this.templateEngine = templateEngine;

        this.transporter = nodemailer.createTransport({
            host: host,
            port: port,
            secure: secure,
            auth: {
                user: username,
                pass: password
            }
        });

    }

}

What is the best way to pass arguments in the constructor using injection or other methods?

Answer №1

There are several ways you can approach this situation.

A) Implementing the Configuration Injection

constructor(
    @inject(TYPES.TemplateEngine) templateEngine: TemplateEngineInterface,
    @inject(TYPES.MailerConfig) @named("host") host: string,
    @inject(TYPES.MailerConfig) @named("port") port: number,
    @inject(TYPES.MailerConfig) @named("secure") secure: boolean,
    @inject(TYPES.MailerConfig) @named("username") username: string,
    @inject(TYPES.MailerConfig) @named("password") password: string
) {

The defined type is:

const TYPES = { MailerConfig: Symbol.for("MailerConfig") }

Here are the corresponding bindings:

type MailerConfig = string|boolean|number;

container.bind<MailerConfig>(TYPES.MailerConfig)
         .toConstantValue("localhost")
         .whenTargetNamed("host");

container.bind<MailerConfig>(TYPES.MailerConfig)
         .toConstantValue(2525)
         .whenTargetNamed("port");

container.bind<MailerConfig>(TYPES.MailerConfig)
         .toConstantValue(true)
         .whenTargetNamed("secure");

container.bind<MailerConfig>(TYPES.MailerConfig)
         .toConstantValue("root")                                         
         .whenTargetNamed("username");

container.bind<MailerConfig>(TYPES.MailerConfig)
         .toConstantValue("toor")                                    
         .whenTargetNamed("password");

Additional Tip: Simplifying the Process

To streamline this task, consider creating helper functions to minimize the repetitive steps required.

Bindings

type MailerConfig = string|boolean|number;

const bindMailerConfig = (ctr: Container, key: string, val: MailerConfig) =>
        ctr.bind<MailerConfig>(TYPES.MailerConfig)
       .toConstantValue(key)
       .whenTargetNamed(val);

bindMailerConfig(container, "localhost", "host");
bindMailerConfig(container, 2525, "port");
bindMailerConfig(container, true, "secure");
bindMailerConfig(container, "root", "username");
bindMailerConfig(container,"toor", "password");

Decorators

const injectNamed = (typeId: any) => (name: string) =>
    (target: any, targetKey: string, index?: number) => {
        inject(typeId)(target, targetKey, number);
        named(name)(target, targetKey, number);
    };

const injectMailerConfig = injectNamed(TYPES.TemplateEngine);

constructor(
    @inject(TYPES.TemplateEngine) templateEngine: TemplateEngineInterface,
    @injectMailerConfig("host") host: string,
    @injectMailerConfig("port") port: number,
    @injectMailerConfig("secure") secure: boolean,
    @injectMailerConfig("username") username: string,
    @injectMailerConfig("password") password: string
) {

B) Multi-step Initialization Approach

@injectable()
export class NodeMailer implements MailerInterface {

    private transporter: any;
    private templateEngine: TemplateEngineInterface;

    constructor(
        @inject(TYPES.TemplateEngine) templateEngine: TemplateEngineInterface,
    ) {
        this.templateEngine = templateEngine;
        this.transporter = null;
    }

    public initialize(host, port, secure, username, password) {
        if (this.transporter) {
            return this.transporter;
        }
        this.transporter = nodemailer.createTransport({
            host: host,
            port: port,
            secure: secure,
            auth: {
                user: username,
                pass: password
            }
        });
        return this.transporter;
    }

    public send() {
        if (this.transporter === null) {
            throw new Error("You must invoke initialize!");
        }
        // ...
    }

}

This multi-step initialization strategy can align well with a factory mechanism.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Developing with TypeScript - Utilizing the <reference path="....."> directive

Recently, I encountered an issue while adding a plugin to the TypeScript compiler. After including my code and compiling tsc.ts, it compiled without any errors. However, when I attempted to run it, I noticed that some variables declared in io.ts were missi ...

Utilizing query parameters in JavaScript

When querying data from the database using passed parameters, I encountered an issue. For example: http://localhost:3030/people?$skip=0&$limit=25&$sort[name]=0&description[$name]=rajiv I wanted to add an extra parameter without including it in ...

The use of the .reset() function in typescript to clear form data may lead to unexpected

I've been trying to use document.getelementbyID().reset(); to reset form values, but I keep running into an error in TypeScript. Property 'reset' does not exist on type 'HTMLElement'. Here's how I implemented it: const resetB ...

Ways to get into the Directive class

@Directive({ selector: '[myHighlight]' }) export class HighlightDirective { static test: number = 5; constructor(private el: ElementRef) { } highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; } } In re ...

Is array.length access cached by NodeJS?

Lately, I've been pondering whether accessing the array.length getter is cached by NodeJS. I've searched for conclusive answers about JS interpretation in browsers, but since I am working on apps in Typescript, that information does not directly ...

Discovering the data types for node.js imports

When starting a node.js project with express, the code typically begins like this - import express = require('express') const app = express() If I want to pass the variable 'app' as a parameter in typescript, what would be the appropri ...

Tips for reusing a Jest mock for react-router's useHistory

When testing my code, I set up a mock for the useHistory hook from react-router-dom like this: jest.mock("react-router-dom", () => ({ useHistory: () => ({ length: 13, push: jest.fn(), block: jest.fn(), createHref: jest.fn(), go ...

What is the best way to retrieve class members using component properties?

I am looking to implement a mixin for setting the header and meta data in my project. I recently discovered vue-meta, which seems to work really well for this purpose. However, I am still getting acquainted with TypeScript and class-based components. How ...

Is there an issue with this return statement?

retrieve token state$.select(state => { retrieve user access_token !== ''}); This error message is what I encountered, [tslint] No Semicolon Present (semicolon) ...

React does not allow _id to be used as a unique key

When I retrieve the categories from my allProducts array fetched from the database using redux-toolkit, I filter and then slice the array for pagination before mapping over it. However, I keep encountering a warning: Each child in a list should have a un ...

I possess an item, but unfortunately, I am only able to save the first object from this possession

I have an object, but I can only save the first item from this object. Interface: export interface PhotoToCreate { albumName: string; albumTitle: string; ImageNameO : string; imageNameT : string; } Component import { Component, OnI ...

React and Typescript Multimap Approach

I'm a beginner in TypeScript and I am struggling to understand how to create a multimap. The code I have is shown below. My goal is to loop through the itemArray and organize the items based on their date values. I want to use the date as the key for ...

Vetur is experiencing issues with template functionality, such as not properly checking data and methods

I have incorporated the vetur extension into my Visual Studio Code for a Vue project using TypeScript. I recently discovered that vetur has the capability to perform type checks within the template tag for props, methods, and even variables. However, in ...

The error type currently displayed relates to window['angularComponentReference']

Currently, I am attempting to incorporate NgZone into my Angular project: constructor( private fishboneService: FishboneService, private zone: NgZone, ) { window['angularComponentReference'] = { zone: this.zone, componentFn: (val ...

What methods can I use to combine existing types and create a brand new one?

Is there a way to combine existing types to create a new type in TypeScript? `export type Align = 'center' | 'left' | 'right' export type Breakpoints = ‘sm’ | ‘md’` I want to merge the Align and Breakpoints types to ...

A Vue component library devoid of bundled dependencies or the need for compiling SCSS files

My current challenge involves the task of finding a way to publish our team's component library. These components are intended to be used by various internal applications within our organization. I have specific requirements: The library must be acc ...

The Standalone Component does not appear for debugging in webpack:source when utilizing an incompatible version of Node

I have developed two components: https://i.sstatic.net/fSNqa.png However, after running ng serve, I am only able to see one component in the source of the Chrome browser: https://i.sstatic.net/VzdDS.png How can I troubleshoot this standalone component? ...

Easy steps to bring in type definitions from an npm package using Vite

I am currently developing a package with several ts functions that will be utilized by multiple repositories, including mobile and web applications. In our team, we use vite as our primary build tool, which is also integrated into the repository. Below is ...

Cannot display data in template

After successfully retrieving JSON data, I am facing trouble displaying the value in my template. It seems that something went wrong with the way I am trying to output it compared to others. My function looks like this, getUserInfo() { var service ...

Unable to connect input with abstract classes at a hierarchy depth of 2 levels or more

When working on my Angular application: If a Component utilizes an Input that is defined in its immediate parent (abstract) class, everything runs smoothly. However, if a Component uses an Input that is declared in a parent class located two levels a ...