Use decorators with external classes

My DI container class mandates that the classes to be resolved must be decorated with an @Injectable() decorator. This is necessary for the decorated class to emit metadata and allow for further configuration, similar to how Angular DI functions (https://angular.io/guide/dependency-injection).

When attempting to register third-party classes into my container, I encounter a dilemma where I need to apply the @Injectable decorator to classes that I am unable to modify. Consider scenarios like incorporating a logger library from node_modules that I want in my container.

Is there a way to add decorators to third-party classes? I'm exploring possibilities such as:

import {Container, Injectable} from "./Container";
import {Logger} from "@vendor/logger";

const container = new Container();

container.register(Logger, { useClass: Logger }); // not feasible due to lack of decoration on Logger
container.register(decorate(Logger, Injectable), { useClass: Logger }); // exploring options to decorate a class with a specific decorator

The current solution involves creating a decorated proxy class that extends from the real logger class. However, this approach of proxying all third-party classes seems overly burdensome.

Any innovative suggestions?

Adding more context:

The @Injectable decorator is vital because undecorated classes fail to emit necessary metadata, making it impossible for the container to interpret and resolve dependencies. It essentially includes a metadata key in the decorated class labeled as __INJECTABLE set to true - when registering new services in the container, it verifies if the __INJECTABLE flag is present and raises an error if not.

Below is an example of a proxy class:

import {Container, Injectable} from "./Container";
import {Logger as ParentLogger} from "@vendor/logger";

// Proxy
@Injectable()
class Logger extends ParentLogger {
}

const container = new Container();
container.register(Logger, { useClass: Logger });

This approach should work theoretically, but necessitates creating proxies for nearly every third-party class.

Answer №1

If you want to implement the decorate function, here is an example:

function CustomDecorator() { return (_: any) => {} }

class BaseUtility { }

// Applying Custom Decorator
@CustomDecorator()
class Utility extends BaseUtility {
}

function decorateComponent<T extends { new(...args: any[]): any }>(sourceClass: T): T{
    @CustomDecorator()
    class decoratedClass extends sourceClass
    {
        constructor(...args: any[]) {
            super(...args);
        }
    }
    return decoratedClass;
}

const DecoratedUtility = decorateComponent(BaseUtility);

Check out the live demo here.

Answer №2

Just sharing my experience in case it helps someone else facing a similar issue. I ran into problems with third-party apps that were using base classes from other third-party libraries, so I had to resort to this workaround.

decorate(injectable(), Object.getPrototypeOf(Events.EventEmitter));

Furthermore, I stumbled upon a solution that allows you to bypass this check altogether, eliminating the need to make the base classes from third-party libraries injectable. Surprisingly, it still functions properly without them being marked as Injectable.

const container = new Container({ skipBaseClassChecks: true });

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

File handling in Angular 2 using Typescript involves understanding the fundamental syntax for managing files

Would someone be able to explain the fundamental syntax for reading and writing text files, also known as file handling in TypeScript? If there is a corresponding link that anyone could provide, it would be greatly appreciated. ...

What could be causing the error "Type 'String' cannot be used as an index type" to appear in my TypeScript code?

My JavaScript code contains several associative arrays for fast object access, but I'm struggling to port it to TypeScript. It's clear that my understanding of TypeScript needs improvement. I've searched for solutions and come across sugges ...

Tips for linking my TypeScript document to the server

Recently diving into the world of Angular 2 and seeking to grasp its intricacies. Currently utilizing Visual Studio Code. How can I have the server monitor changes in the TypeScript file? Do I need a compiler to convert it to JavaScript for this purpose? ...

Click event to verify, delete, and include class identifier in angular13

Looking to enhance functionality by dynamically adding and removing the 'active' class to 'li a' elements on click. While the current code performs well when clicking from top to bottom, it fails to work in reverse order. component.htm ...

Develop a Vaadin 14 component in Typescript that allows for sound recording functionality

I am currently working on developing a simple Vaadin component, similar to a Button, that will respond to both press and release events. Specifically, upon pressing the button, it should begin recording sound from the microphone, and upon release, it shoul ...

Steps to adding an item to a TypeScript array

My code involved creating an array called dataArray of type dataRows, which is an interface. dataArray: Array<dataRows>; In a function, I attempted to push a dataRows object into the array like this: this.dataArray.push(row) But for some ...

Received a 'Vue error: Redundant navigation to current location prevented' when attempting to refresh the page with updated parameters

I'm attempting to refresh the page with new parameters when the state changes: @Watch('workspace') onWorkspaceChanged(o: Workspace, n: Workspace){ if(o.type === n.type && o.id === n.id) return; this.$router.push({name: this.$rout ...

Using the keyof operator to determine the data type of a property within a TypeScript class

According to TypeScript's documentation on the keyof operator, you can access a property of an object instance using this function below. function getProperty<T, K extends keyof T>(o: T, name: K) { return o[name]; } If you want to obtain th ...

How to retrieve class constants in TypeORM using getManager.query() in Typescript

I'm diving into the world of typeORM and Typescript. I've created a service class to handle a specific resource: import { Injectable} from '@nestjs/common'; import { getManager } from 'typeorm'; class MyServiceClass { static ...

Issue: Trying to emit before Angular Webpack plugin has been initialized (Error: Emit attempted before Angular Webpack plugin initialization)

Currently, I am working on a project in Angular 15 where I am migrating code from Angular version 5. Despite fixing all the errors, I'm facing one particular issue. Here are the details of my current Angular version: Angular CLI: 15.2.10 Node: 18.18. ...

Encountering an error when trying to access the initial child element in TypeScript

In my TypeScript code, I am attempting to retrieve the first child element of each list item. However, TypeScript keeps throwing an error message that says: Type 'Element' is missing the following properties from type 'HTMLUListElement' ...

Incorrect Angular Routing Component OpeningI am experiencing an issue where

I am facing an issue with lazy loading a module, where it is not correctly displaying the desired component. Even though the route seems correct, it shows a different component instead. https://i.sstatic.net/v4oAB.png Despite specifying the path for "Pus ...

What is the most efficient method for designing a nested object with two levels of enums, allowing for some enum values to be optional?

As an illustration enum Size { Small: 'smallSize' Medium: 'mediumSize' Large: 'largeSize' } enum Weekday { Monday: 'Monday' Tuesday: 'Tuesday' ... } // Need only Size.Medium and Size.Large, a ...

Encountering a non-constructor issue in Vue, while working with Typescript and webpack

I'm currently using Typescript along with Webpack. Below is my code snippet. When I run it in Chrome, I encounter the following error: Uncaught TypeError: vue_1.default is not a constructor at Object.defineProperty.value (Index.ts:3) at __web ...

Retrieving JSON data with Typescript

https://i.sstatic.net/i5vFM.jpg I recently received some data from a request, expecting to return a JSON object that I can utilize in HTML. However, upon running console.log(this.data); I noticed that there are 20 elements in the articles array, but it ...

Utilizing the Redux Connect HOC's wrapped component type in React.RefObject without the need for re-importing

My current setup involves a simple component that is wrapped with react-redux and has a ref with forwardRef: true, demonstrated below: // Button.tsx class Button extends React.Component { // ... } // ... export default connect(mapStateToProps, null, n ...

Is it possible to house the reusable component's service in the core module within Angular based on the specified scenario?

├── src │ ├── app │ │ ├── core │ │ │ ├── services │ │ │ ├── core.module.ts │ │ ├── shared │ │ │ ├── produt-form │ │ │ │ ├── product-form.component.ts|h ...

How to Embed HTML Tags in a TypeScript File in Angular

I'm currently utilizing Angular2 and I'm looking to incorporate an HTML tag inside the return function in a TypeScript file. tooltip: (param: any) => { return `<span> ${param.value} </span>`; } I've attempted ...

Generate the return type dynamically based on an array parameter containing objects in TypeScript

Consider the following TypeScript definition: interface Configuration { field: string } function bar<T extends Configuration>(arr: T[]): Record<T['field'], undefined> function bar(arr: Configuration[]): Record<string, undefine ...

Issues with Angular2 http.get() returning 404 errors consistently

I'm encountering an issue while attempting to load dummy JSON data from a file using angular2 http.get method. It appears that the method is unable to retrieve the data, consistently returning a 404 status code for resource not available. Below is the ...