Subclasses do not have the ability to invoke methods and properties

My intention is to utilize different low-level implementations in various environments while abstracting the specific details from higher-level modules.

The expectation is to have a standard way of using the class on the outer layer.

The buildEnv variable comes into play when utilizing rollupJs for building.

class subA {
  protected fn() {
    console.log('subA');  
  }
}
class subB {
  protected fn() {
    console.log('subB');  
  }
}

const buildEnv = 'A';
function changeSubClass() {
   return buildEnv === 'A' ? subA : subB;
}

const BaseClass = changeSubClass();
class MainClass extends BaseClass {
  constructor() {
    supper();
    this.fn(); // <--- An error is reported here
  }
}

// The desired use:
const clsIns = new MainClass();

One approach involves having subA inherit from MainClass, however, for external usage, an instance needs to be obtained by calling the changeSubClass method:

const buildEnv = 'A';
function createSubClass() {
   const Cls = buildEnv === 'A' ? subA() : subB;
   return new Cls();
}

class subA extends MainClass {
  protected fn() {
    console.log('subA');  
  }
}
class subB extends MainClass {
  protected fn() {
    console.log('subB');  
  }
}

class MainClass implements ISubCls {
  constructor() {
    this.fn();
  }
}

interface ISubCls {
  fn: () => void
}

This method of utilization is not ideal for my requirements.

const clsIns = createSubClass();

Answer №1

It seems like your approach to object-oriented programming may need some clarity. If I understand correctly, you are attempting to choose a class implementation to instantiate based on the program's environment.

Instead of using inheritance, I suggest favoring composition. Take a look at the code snippet below:


import { makeSendMailUseCase } from "./make-email-service";

async function main() {
    const sendMail = await makeSendMailUseCase();
    const result = await sendMail.execute("<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="7b161e3b1314080f55181416">[email protected]</a>", "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="03776c436b6c70772d606c6e">[email protected]</a>", "testing my email", "Hello ");

    if (result) console.log(' email was sent ')
    else throw new Error('failed sending mail')
}

main().catch(console.log.bind(console))
  

In this code snippet, the application aims to send an email by instantiating a SendMailUseCase class to manage this task. To send the email, it requires an external service. This service is injected into the use case, making use of the factory pattern to determine which service will be used for consumption. As a result, the main program remains unaware of the specific service being utilized.


import { createTestAccount } from "nodemailer";
import { SendEmailUseCase } from "./sendmail-usecase";
import { ProductionEmailService } from "./real-email-service";
import { FakeEmailService } from "./fake-email-service";
import { IEmailService } from "./email-service";

export async function makeSendMailUseCase() {
    const isProduction = process.env.NODE_ENV === 'production';

    let emailService: IEmailService;

    if (isProduction) {
        const testAccount = await createTestAccount();
        emailService = new ProductionEmailService(testAccount)
    } else {
        emailService = new FakeEmailService();
    }

    return new SendEmailUseCase(emailService);
}

The makeSendMailUseCase function serves as a factory method to provide a new instance of the SendMailUseCase, selecting the appropriate implementation of the IEmailService based on the current environment.


import { EmailFormat, IEmailService } from "./email-service";

export class SendEmailUseCase {
    constructor(private emailProvider: IEmailService) { }

    execute(from: string, to: string, subject: string, content: string): Promise<boolean> {
        const params = {
            from, to, subject, content, format: EmailFormat.Text
        }

        return this.emailProvider.sendEmail(params);
    }
}


The SendEmailUseCase delegates the email-sending task to the concrete implementation provided in its constructor.


export enum EmailFormat {
    Text,
    HTML
}

export type EmailOptions = {
    from: string;
    to: string;
    subject: string;
    content: string;
    format: EmailFormat
}

export interface IEmailService {
    sendEmail(options: EmailOptions): Promise<boolean>;
}

Next up is the implementation of the FakeEmailService:


import { EmailOptions, IEmailService } from "./email-service";

export class FakeEmailService implements IEmailService {
    async sendEmail(options: EmailOptions): Promise<boolean> {
        console.log(options)
        return Promise.resolve(true);
    }
}

This implementation meets the contract requirements by simply fulfilling them without any real action.


import { TestAccount, Transporter, createTransport } from "nodemailer";
import { EmailFormat, EmailOptions, IEmailService } from "./email-service";

export class ProductionEmailService implements IEmailService {
    private readonly transporter: Transporter

    constructor(account: TestAccount) {
        this.transporter = createTransport({
            host: account.smtp.host,
            port: account.smtp.port,
            secure: account.smtp.secure,
            auth: {
                user: account.user, // generated ethereal user
                pass: account.pass, // generated ethereal password
            },
        });
    }

    async sendEmail(options: EmailOptions): Promise<boolean> {

        const info = await this.transporter.sendMail({
            from: options.from,
            to: options.to,
            subject: options.subject,
            text: options.format === EmailFormat.Text ? options.content : "",
            html: options.format === EmailFormat.HTML ? options.content : "",
        });

        return info;
    }
}

The production email service relies on the nodemailer library to handle email delivery.

That wraps it up!

Answer №2

// Bringing in
// prettify-ignore
interface Logger { info: (msg: string) => void }
// prettify-ignore
class ConsoleLogger implements Logger { info(msg: string): void { console.log("[Console]:", msg) } }
// prettify-ignore
class PinoLogger    implements Logger { info(msg: string): void { console.log("[Pino]:"   , msg) } }

// Section 1: Business Elements
interface UserData {
  name: string
}

class AuthService {
  async getUserData(): Promise<UserData> {
    return { name: "Big Lebowski" }
  }
}

class User {
  constructor(private data: UserData) {}
  name = () => this.data.name
}

class PaymentService {
  constructor(private readonly logger: Logger, private readonly user: User) {}
  sendMoney() {
    this.logger.info(`Sending money to the: ${this.user.name()} `)
    return true
  }
}

// Phase 2: Manual Dependency Injection / composition root
export async function runMyApp() {
  const logger =
    process.env.NODE_ENV === "production"
      ? new PinoLogger()
      : new ConsoleLogger()

  const auth = new AuthService()
  const user = new User(await auth.getUserData())

  const paymentService = new PaymentService(logger, user)
  paymentService.sendMoney()
}

console.log(" ---- My Application BEGIN \n\n")
runMyApp().then(() => {
  console.log("\n\n ---- My Application END")
})

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

Displaying a collection of objects in HTML by iterating through an array

As someone new to coding, I am eager to tackle the following challenge: I have designed 3 distinct classes. The primary class is the Place class, followed by a restaurant class and an events class. Both the restaurant class and events class inherit core p ...

Generating images with HTML canvas only occurs once before stopping

I successfully implemented an image generation button using Nextjs and the HTML canvas element. The functionality works almost flawlessly - when a user clicks the "Generate Image" button, it creates an image containing smaller images with labels underneath ...

Create seamless communication between Angular application and React build

I am currently engaged in a project that involves integrating a React widget into an Angular application. The component I'm working on functions as a chatbot. Here is the App.tsx file (written in TypeScript) which serves as the entry point for the Rea ...

Attempting to develop a server component that will transmit a JSON result set from a MySQL database located on the local server. What is the best method to send the server object (RowDataPacket) to the

After successfully rendering server-side pages and creating forms with react hooks for database updates, I encountered a challenge in integrating Ag-Grid into my application. Despite being able to retrieve data from the database using the mysql2 module and ...

Combining React Context and Typescript using a Custom Hook

I have been working on a context provider in React and Chakra-UI, but I seem to be facing some issues: import { useBoolean } from "@chakra-ui/react" import { createContext } from "react" const MobileContext = createContext<typeof us ...

Resolving Angular Issue: Error code (5, 12) TS2314 - Solving the requirement for 1 type argument in the 'Array<T>' generic type

Encountered an issue in the JSON data within my typescript file. I'm working on creating an Angular API that performs a git-search operation. Initially, I had the JSON data set up correctly but then I modified all data values to their respective data ...

In the function ngOnChange, the SimpleChange currentValue is supposed to be defined, but for some reason, the

Within the TypeScript code, a name variable is assigned input from another component. @Input() name : any ; In the ngOnChange function, I retrieve and print the SimpleChange object in the following manner. ngOnChanges(changes: SimpleChange){ console.l ...

Strategies for managing the "ref" property of Material-UI component props within our custom components

I have a unique custom component setup as shown below: // ... import { PaperProps, styled } from '@mui/material'; type ComponentProps = PaperProps & { a: string, b: string }; export const MyPaper = styled(Paper)(/* ... */); const Compo ...

"Error: The update depth has exceeded the limit while trying to use the

I've been working on implementing localStorage in NextJs using TypeScript by following this guide at , but I am encountering an error. https://i.sstatic.net/NX78a.png Specifically, the error occurs on the context provider repeatedly. Below is the c ...

Using optional chaining with TypeScript types

I'm dealing with a complex data structure that is deeply nested, and I need to reference a type within it. The issue is that this type doesn't have its own unique name or definition. Here's an example: MyQuery['system']['error ...

Creating a Record instance consisting of a specific key and its corresponding value

Sorry for the complexity, I've struggled to simplify this further. Feel free to update the question title for more specificity. I aim to define a foundational data type structure: type AbstractBaseTypes = { [key: string]: { inputTypes ...

Unable to modify the Jest mock function's behavior

The issue I am facing involves the following steps: Setting up mocks in the beforeEach function Attempting to modify certain mock behaviors in specific tests where uniqueness is required Encountering difficulty in changing the values from the in ...

Using "array_agg" in a "having clause" with Sequelize

I am facing a particular scenario with my database setup. I have three tables named computers, flags, and computerFlags that establish relationships between them. The structure of the computerFlags table is as follows: computerName | flagId computer1 | ...

Embarking on a new undertaking with Visual Studio 2015 and diving into the world of Angular

My journey to getting Angular2 working in Visual Studio 2015 Pro involved a lot of trial and error, but I eventually found a setup that worked for me. Despite the abundance of instructions out there, I struggled to find clear answers tailored specifically ...

Is it possible to optimize the performance of my React and TypeScript project with the help of webpack?

I am working on a massive project that takes 6 to 8 minutes to load when I run npm start. Is there a way to speed up the loading process by first displaying the sign-in page and then loading everything else? ...

Following the npm update, encountering errors with webpack

Upgrading the npm package to version 8.2.0 has caused issues in my React application. Here is a screenshot of the problem: https://i.stack.imgur.com/noQIz.png These are the error messages I see in the console: [HMR] Waiting for update signal from WDS.. ...

A versatile method for transforming enums into arrays that can handle null values

Looking for a simpler way to create a TypeScript function that converts an enum to an array, including support for null values. Here's an example of what I'm trying to achieve: enum Color { RED = "Red", GREEN = "Green&qu ...

I'm stuck trying to figure out all the parameters for the MapsPage component in Angular 2

Currently, I am utilizing Angular2 with Ionic2 for my mobile app development. Everything was working flawlessly until I decided to incorporate a new module for Google Maps navigation. Specifically, I am using phonegap-launch-navigator for this purpose. The ...

What is the best way to create a Typescript type consisting of only the public members of a different type?

Inside the realm of Typescript 4.3.5 In what manner can I establish a type that consists solely of the public members and properties of another type? Take into account: class Thing { public name: string private secret: string public greet(): string ...

The functionality of arguments in the whenAllDone promise/deferred javascript helper seems to fail when attempting to encapsulate existing code within a Deferred

My goal is to implement the solution provided in TypeScript from Stack Overflow: UPDATE 2 - The issue with the original answer is that it does not support a single deferred. I have made modifications to reproduce the error in his fiddle. http://jsfiddle.n ...