TypeScript class featuring a unique method that is not utilized in every instance

I have a TypeScript class that is managing ZMQ bus communication. Initially, I created a general class that can be instantiated multiple times. However, now I need to create instances with slight variations that do not perfectly fit the generic class I originally made. The only difference is that in these new instances, the use of the request method depends on the value of type. Is there a way to maintain the same general class structure but exclude the request method when it is not needed?

let zmq = require("zeromq")

class ZMQBus {
    socket:any
    port:number

    constructor(type:string, port:number) {
        this.socket = zmq.socket(type)
        this.port   = port
    }

    open(): void {
        this.socket.connect("tcp://localhost:" + this.port)

        this.socket.on("message", function(reply) {
            console.log("Received reply : " + reply.toString());
        })
     }
    
     // Exclude request method based on 'type'

    close(): void {
        this.socket.close();
    }
}

Answer №1

It was mentioned that avoiding two classes to prevent implying differences between them. However, the insignificance of a request in certain scenarios does not equate to harm. A solution could involve utilizing a single class with a conditional implementation of request, where it is only utilized when necessary. If the absence of request must be explicitly stated, a separate type can be created. By employing a factory pattern and conditional types, semantic clarity can be achieved without falling into any anti-patterns.

type ZmqTypes = "REQUEST" | "NOREQUEST";

interface ZMQBus {
  open: () => void;
  close: () => void;
  request?: (msg: any) => void;
  type: ZmqTypes;
  port: number;
}

interface ZMQBusR extends ZMQBus {
  request: (msg: any) => void;
}

interface ZMQBusNoR extends Omit<ZMQBus, "request"> {}

class GenericZMQBus implements ZMQBus {
    socket:any
    port:number

    constructor(type:string, port:number) {
        this.socket = zmq.socket(type)
        this.port   = port
    }

    open(): void {
        this.socket.connect("tcp://localhost:" + this.port)

        this.socket.on("message", function(reply : any) {
            console.log("Received reply : " + reply.toString());
        })
    }

    close(): void {
        this.socket.close();
    }
}

class ZMQBusWithRequest extends GenericZMQBus implements ZMQBusR {
  request(msg : any) {
    console.log(msg);
  }
}

function createZMQBus<T extends ZmqTypes>(type: T, port: number) : T extends "REQUEST" ? ZMQBusR : ZMQBusNoR {
  if (type === "REQUEST") { 
    return new ZMQBusWithRequest(type, port) as unknown as T extends "REQUEST" ? ZMQBusR : ZMQBusNoR;
  }
  return new GenericZMQBus(type, port) as unknown as T extends "REQUEST" ? ZMQBusR : ZMQBusNoR;
}

const zmqObj = createZMQBus("REQUEST", 999);
const zmqObj1 = createZMQBus("NOREQUEST", 999);

zmqObj.request('hello');
zmqObj1.request('error'); // results in an error

This approach retains multiple classes and interfaces within a strict type system, but offers a unified method for creating objects that signifies a consistent purpose, even if implementations vary. Both extensions of a shared generic interface enable passing either type as a single ZMQType. It is essential to verify the presence of the request method before usage.

function testZMQImplementation(z: ZMQBus) {
  if (z.request) {
    z.request('testing')
  }
}

testZMQImplementation(zmqObj);
testZMQImplementation(zmqObj1);

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

Is there a way to retrieve the modal's viewport height in Angular?

Is it possible to determine the viewport height of my ng bootstrap modal within my Angular application? Here is what I currently have: I have a modal with CSS styling as shown below: .modal-xxl { width: 95% !important; max-height: 90% !important; ...

The combination of UseState and useContext in React Typescript may lead to compatibility issues

I attempted to integrate the context API with the useState hook but encountered difficulties when using TypeScript. First, let's create App.tsx: const App = () => { const [exampleId, updateExampleId] = useState(0); return ( <div> ...

Merging Promises in Typescript

In summary, my question is whether using a union type inside and outside of generics creates a different type. As I develop an API server with Express and TypeScript, I have created a wrapper function to handle the return type formation. This wrapper fun ...

What is the solution to the error message "Uncaught TypeError: createTheme_default is not a function"?

While working on my react application with vite, typescript, and mui, I encountered the following error: enter image description here This issue seems to be connected to material ui. Sometimes, deleting the 'deps' folder in '\node_mod ...

Determine the type of the final function within a variable number of nested closures

Imagine you have a function like this: const f = a => b => ... x => { return somevalue } Is there a way to determine the type of just the final function typeof x => { return somevalue } even if we don't know how many closures come before ...

bespoke arguments for the super function in a subclass of Angular

I am attempting to incorporate the ol sidebar from umbe1987/Turbo87 into an Angular project. As I extend a class, I find myself needing to manipulate constructor parameters in the derived class constructor before passing them to the superclass constructor ...

Dependencies exclusively for NPM post-installUnique Rewrite: "N

I have been using git to distribute an internal TypeScript NPM package. To avoid cluttering my repository with build files, I have implemented a postinstall action to build the package upon installation: "postinstall": "tsc -p tsconfig.json& ...

The function record.factory does not exist

Here is the code for the AppComponent: import { Component, OnInit } from '@angular/core'; import { APICommunicationService } from './api-comm/api-communication.service'; import { Observer } from 'rxjs'; @Component({ sel ...

Creating a task management application using Vue 3 Composition API and Typescript with reactivity

I am in the process of creating a simple todo list application using Vue 3 Composition API and TypeScript. Initially, I set up the function for my component to utilize the ref method to manage the reactivity of user input inserted into the listItems array. ...

Problem with Extending Jest Matchers in VS Code TypeScript

I've developed unique Jest matchers to enhance expect for handling AxiosResponse objects. Although I've followed the standard method for expanding Jest's matcher types, my custom matchers are not being recognized by TypeScript. The error di ...

SonarLint is suggesting that the parameter currently in use should either be removed or renamed

According to Sonar lint: Suggestion: Remove the unused function parameter "customParams" or rename it to "_customParams" for clearer intention. However, the parameter "customParams" is actually being used in the method "getNextUrl". What am I doing wron ...

Incorporate a stylish gradient background into react-chartjs-2

I am currently working on adding a gradient background with some transparency to a radar graph within a react component. So far, I have only found solutions that involve referencing a chartjs canvas in an html file, but none for implementing it directly in ...

What is the best way to shift focus to the next input field once the character limit has been reached, especially when the input is contained

My challenge lies in having six input fields arranged side by side in a single row: In my component.html file: onDigitInput(event: any) { let element; if (event.code !== 'Backspace') element = event.srcElement.nextElementSibling; consol ...

Develop a directive for transforming data

In my latest project, I am looking to develop a LoaderDirective that can fetch an observable, display a spinner while loading the data, and then switch to showing the actual data once loaded. I also want it to expose the loaded data using the 'as&apos ...

The chosen index in the Material Stepper feature is experiencing a malfunction

I've been busy working on a Mat-Stepper, actually two of them. I have a component that contains two ng-templates set up like this: Question: Why is my selected index not functioning as expected? Am I missing something? I know you can define [selected ...

Having trouble adding custom props to MUI-Material ListItemButtonProps? Facing a TypeScript error?

This particular task should be relatively straightforward. I am seeking a custom component that inherits all props from ListItemButtonProps, while also adding an extra state prop: type SidebarListItemButtonProps = ListItemButtonProps & { state: Sideb ...

Can you clarify the meaning of "int" in this code snippet?

What does the ?: and <I extends any[] = any[]> signify in this context, and how is it utilized? export interface QueryConfig<I extends any[] = any[]> { name?: string; text: string; values?: I; types?: CustomTypesConfig; } ...

Updating the Angular2 function in the main app component causes the current component to be reset

I developed an application that has the following structure: app.component.ts import { Component } from 'angular2/core'; import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router'; import { NgClass } from &apos ...

Tips for ensuring that the DOM is fully rendered before executing any code

Before proceeding to the next functions, it is necessary to wait for the DOM to finish rendering. The flow or lifecycle of this process is outlined below: Adding an item to the Array: this.someFormArray.push((this.createForm({ name: 'Name& ...

Execute a selector on child elements using cheerio

I am struggling to apply selectors to elements in cheerio (version 1.0.0-rc.3). Attempting to use find() results in an error. const xmlText = ` <table> <tr><td>Foo</td><td/></tr> <tr><td>1,2,3</td> ...