Utilizing decorators to specify unique functionalities within a class by providing details on how class overrides should be

By crafting a TypeScript class Decorator function, I have enabled the capability to adorn similar classes. Should a class find itself with a Decorator perched above it, said class will delve into the stored values of the Decorator for the highest priority class in its category. If the newly adorned class boasts a lower priority than the current highest priority class, it will be promoted to the status of that stored class.

In essence, all decorated classes within the same category should ultimately mirror the highest priority class.

For instance, envision having GeneralClassA (priority 0), SpecificClassA (priority 1) and ClassA (priority 0). All these classes ought to point towards a singular class, specifically, the SpecificClassA, owing to its superior priority level.

The strategy is to preset General classes with decorations while affording developers the freedom to define Specific classes and decorate them as higher priority entities.

While everything appears to function properly, there persists an issue where the decorated highest priority class must be declared PRIOR to any other decorated classes. Failing that sequence may lead to undecorated classes retaining precedence over previously designated classes.

Is there a method to retroactively supersede all decorated classes (by storing their constructors) once the higher priority class has been invoked and adorned? Or perhaps a means to ensure the declaration of all Specific classes precedes every other initialization?

I have reproduced my predicament here: StackBlitz

My attempt involved saving all decorated classes and attempting to merge one constructor and prototype to another once the high priority class was adorned. Unfortunately, this approach failed. Even if successful, what happens if the lower priority class had already been utilized prior to declaring the higher priority class (the actual class declaration transpires upon use during import).

I seek suggestions on initializing (declaring) all Specific classes preemptively, possibly even before the main.js file ever executes.

The specific decorator in question:

export const YIELD_LIST: {
  [name: string]: { ctor: any; priority: number };
} = {};

type Constructable<T> = new (...args: any[]) => T;

export function Yield<T extends Constructable<any>>(
  name: string,
  priority: number = 0
) {
  return (ctor: T) => {
    if (!YIELD_LIST[name]) {
      YIELD_LIST[name] = { ctor, priority };
    } else {
      if (YIELD_LIST[name].priority <= priority) {
        YIELD_LIST[name] = { ctor, priority };
      }
    }
    const maxPriorityCtor = YIELD_LIST[name].ctor as T;

    return class extends maxPriorityCtor {
      constructor(...args: any[]) {
        super(...args);
      }
    };
  };
}

Implementation example:

@Yield('CLASS_A', 1)
export class SpecificClassA extends GeneralClassA {
  public override value = 10;
}

Answer №1

I successfully implemented the intended functionality by incorporating decorator checker logic. The developer is required to define all expected override classes, and these classes must be designated as overriders for the code to execute. Additionally, both the override and overridden classes must be decorated using the Yield and Yieldable decorator functions respectively.

export const YIELD_LIST: {
  [token: string]: { overrideCtor: any };
} = {};

const EXPECTED_YIELDERS: string[] = [];
const DEFINED_YIELDERS: string[] = [];
function checkYielders() {
  if (!DEFINED_YIELDERS.every((v) => EXPECTED_YIELDERS.includes(v))) {
    throw new Error(
      'Unexpected Yielder was defined. Please add all expected Yielders in main.ts.'
    );
  }

  if (!EXPECTED_YIELDERS.every((v) => DEFINED_YIELDERS.includes(v))) {
    throw new Error(
      'Not all expected Yielders from main.ts were defined. Please Yield all expected Yielders.'
    );
  }
}

export function expectYieders<T extends Constructable<any>>(...ctors: T[]) {
  EXPECTED_YIELDERS.push(...ctors.map((x) => x.name));
  return { checkYielders };
}

type Constructable<T> = new (...args: any[]) => T;

export function Yield<T extends Constructable<any>>(token: string) {
  return (ctor: T) => {
    if (!YIELD_LIST[token]) {
      YIELD_LIST[token] = {
        overrideCtor: ctor,
      };
      DEFINED_YIELDERS.push(ctor.name);
    }
  };
}

export function Yieldable<T extends Constructable<any>>(token: string) {
  return (_ctor: T) => {
    if (YIELD_LIST[token]) {
      return class extends (YIELD_LIST[token].overrideCtor as T) {
        constructor(...args: any[]) {
          super(...args);
        }
      };
    }
  };
}

To ensure appropriate behavior, it's essential to include the following snippet at the beginning:

expectYieders(SpecificClassA).checkYielders();

Now, it's possible to define the base class along with the Yield and Yieldable classes:

//-------class-a.ts--------
export const CLASS_A = 'CLASS_A_YIELD_TOKEN';
export class GeneralClassA {
  public value = 5;

  public showValue() {
    console.log(this.value);
  }
}

//---specific-class-a.ts---
@Yield(CLASS_A)
export class SpecificClassA extends GeneralClassA {
  public override value = 10;
}

//----general-class-a.ts---
@Yieldable(CLASS_A)
export class ClassA extends GeneralClassA {}

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

Prevent auth0 express middleware from causing server crashes by handling failures silently

I am currently integrating auth0 into a node project for authentication using JWTs. Each request to an authenticated endpoint requires a token, and auth0 provided me with this middleware function: import {auth} from 'express-oauth2-jwt-bearer'; i ...

Webpack dev server encounters issues compiling the identical source code that Webpack has no trouble with

I've been attempting to set up webpack-dev-server, but I continually encounter the following error: webpack: Failed to compile. What's puzzling is that I can successfully compile using just webpack. Here are the outputs from both scenarios: We ...

TypeScript compiler encountering issue with locating immutable.js Map iterator within for of loop

I am currently facing a challenge with using immutable.js alongside TypeScript. The issue lies in convincing the TypeScript compiler that a Map has an iterator, even though the code runs smoothly in ES6. I am perplexed as to why it does not function correc ...

What could be the reason behind the glob promise yielding an array with empty strings?

I am currently utilizing TypeScript, ts-node, npm, and path modules My objective is to generate an array of file names based on a specific pattern using glob-promise - an npm package I imported that offers promise-based functionality for the glob module. ...

tips for converting an object in typescript

import { Enseignant } from "./Enseignant"; import { AlreadyExistsError } from "./errors/AlreadyExistsError"; import { Etudiant } from "./Etudiant"; export class Utilisateur { private _id: string; private _first_name: string; private _last_name: stri ...

What is the best approach for creating a basic test to evaluate the functionality of MatDialog in Angular 2?

I'm currently working on a component that looks like this: @Component({ selector: 'my-form', templateUrl: './my-form.component.html', }) export class MyFormComponent implements OnInit { @Input('company') company: ...

Troubleshooting React/Jest issues with updating values in Select elements on onChange event

I am currently testing the Select element's value after it has been changed with the following code: it("changes value after selecting another field", () => { doSetupWork(); let field = screen.getByLabelText("MySelectField") ...

Promise rejection not handled: The play() function was unsuccessful as it requires the user to interact with the document beforehand

After upgrading my application from Angular 10 to 11, I encountered an error while running unit tests. The error causes the tests to terminate, but strangely, sometimes they run without any issues. Does anyone have suggestions on how to resolve this issue? ...

Unique TypeScript code snippets tailored for VSCode

Is it possible to create detailed custom user snippets in VS Code for TypeScript functions such as: someArray.forEach((val: getTypeFromArrayOnTheFly){ } I was able to create a simple snippet, but I am unsure how to make it appear after typing an array na ...

Renew subscription following interruption

I need to trigger the updatePosition function when the mouseMove event occurs on the document, but not when it is emitted from the testEl.current element: const cursor$ = fromEvent<MouseEvent>(document, 'cursor') const scroll$ = fromEvent(d ...

Utilizing a string as an argument in a function and dynamically assigning it as a key name in object.assign

Within my Angular 5 app written in TypeScript, I have a method in a service that requires two arguments: an event object and a string serving as the key for an object stored in the browser's web storage. This method is responsible for assigning a new ...

Steps for modifying and refreshing the dynamically included input data

Situation: The example below shows a file named contacts in JSON format: [ { "name": "Ailis Wyld", "addresses": [ { "addressType": "Business", "city": "Lansing", "postalCode": "48930" }, ...

How can I specify a subset within an Angular FormGroup?

Let's consider a scenario: I have two forms, form1 and form2, each containing multiple FormControls. The common property among them is the FormControl with an id. Now, I need to pass these forms as arguments to a method that should only require know ...

Retrieving chosen row data in Angular 6 Material Table

I am attempting to send the value of a selected row from one component to another upon clicking a button. However, in this particular example, I'm unsure where to obtain the selected row values and how to pass them on button click. After that, routing ...

Tips for integrating Tinymce in Angular 2 once it has reached its stable version

How to Implement TinyMCE in Angular 2 with Two-Way Binding Working with Third-Party Libraries in Angular 2 After trying multiple solutions provided on stackoverflow, I am still unable to load the TinyMCE editor successfully. I am wondering if there are ...

Tips for adding or updating query parameters in Angular2

When navigating and updating settings in my app, I am utilizing query parameters that I need to retain and update. Adding a parameter is simple using the following method. onNavigate() { this.router.navigate(['reports'], {queryParams: {'rep ...

The object MediaStreamRecorder is not able to be used as a constructor

Just starting my Angular6 journey and I'm experimenting with the MediaStreamRecorder feature. Somehow, I must be messing up the definition of MediaStreamRecorder because all I get is this frustrating error message: TypeError: msr__WEBPACK_IMPORTED_MOD ...

Encountering a module not found error when attempting to mock components in Jest unit tests using TypeScript within a Node.js

I'm currently in the process of incorporating Jest unit testing into my TypeScript-written Node.js application. However, I've hit a snag when it comes to mocking certain elements. The specific error I'm encountering can be seen below: https ...

Issues with the functionality of Angular Firebase Authentication Service

I am currently working on setting up an authentication service in Angular that will integrate with Google Firebase for a Login form. However, I have encountered an issue where including the service in the constructor of my LoginComponent prevents me from a ...

Differentiating Views for a Single URL in Angular 6: Enhancing Progressive Web Apps

Below is the content from my app-router.module.ts import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { DheaderComponent } from './dheader/dheader. ...