ExitDecorator in TypeScript

Introduction:

In my current setup, I have an object called `Item` that consists of an array of `Group(s)`, with each group containing an array of `User(s)`. The `Item` object exposes various APIs such as `addUser`, `removeUser`, `addGroup`, `removeGroup`, `addUnit`, and `removeUnit`. Every time one of these actions is taken, the `rank` property of each user within a certain group needs to be recalculated.

I am seeking a solution where the method `calcRank` is automatically triggered at the end of each API action without the need for explicit invocation. Something akin to an `OnExit` decorator would be ideal.

Answer №1

The Advice Design Pattern shows how to easily implement dynamic behavior in JavaScript:

function beforeAction(modifyBehavior) {
  return function(method) {
    return function beforeHandler(...args) {
      modifyBehavior.apply(this, args);
      return method.apply(this, args);
    };
  };
}

function afterAction(modifyBehavior) {
  return function(method) {
    return function afterHandler(...args) {
      const result = method.apply(this, args);
      modifyBehavior.apply(this, [args, result]);
      return result;
    };
  };
}

const defaultFunc = _ => _;

function applyAdvice({before: beforeModifier = defaultFunc, after: afterModifier = defaultFunc, skip: []}) {
  return function(Class) {
    Object.keys(Class.prototype).forEach(function(methodName) {
      if (!skip.contains(methodName)) {
        const method = Class.prototype[methodName];
        const afterDecorator = afterAction(afterModifier);
        const beforeDecorator = beforeAction(beforeModifier);
        Class.prototype[methodName] = afterDecorator(beforeDecorator(method));
      }
    });
  };
}


@applyAdvice({
  after() {
    this.updateRank();
  },
  skip: ['updateRank']
})
class Item {
  addUser() {}
  removeUser() {}
  addGroup() {}
  removeGroup() {}
  addUnit() {}
  removeUnit() {}
  updateRank() {}
}

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

Accessing an Excel file in TypeScript using the .xlsx format

After extensive research, I managed to find a solution for reading the .xlsx file in a TypeScript environment. Once implemented, I documented the solution along with a question and answer. The file "demo.xlsx" contains UserIds and Code, displayed in the i ...

When an empty array is returned from a catch statement in an async/await method, TypeScript infers it as never

Function getData() returns a Promise<Output[]>. When used without a catch statement, the inferred data type is Output[]. However, adding a catch statement in front of the getData() method changes the inferred data type to Output[] | void. This sugge ...

The tRPC setData hook is limited in its ability to access all data necessary for optimistic UI updates

As I integrate mutations using tRPC and React Query to optimistically update my UI upon adding a new item, I've encountered an issue. The problem lies in the query I'm updating, which requires specific properties like auto-generated IDs or datab ...

The error message "Type 'IPromise<{}>' is not compatible with type 'IPromise<TemplatesPagingModel>' in typescript version 2.8.0" is displayed

Currently, I am working on an AngularJS framework (version 1.5.8) with the latest TypeScript files (version 2.8.0). However, after updating to the most recent TypeScript version, the code below is not compiling. Implementation of Angular interface: inter ...

The declaration file for 'autobind-decorator' is missing in TypeScript and cannot be located

Having a bit of trouble with my Typescript project. I'm trying to import the 'autobind-decorator' package, but I hit a roadblock. When compiling, I keep running into this error: cannot find declaration file for 'autobind-decorator&ap ...

Experiencing a Typescript error when trying to access a property within a nested object

My current challenge involves typing an object, which seems to be error-free until I try to access a nested property and encounter the dreaded red squiggle. After some research, I came across suggestions like this: type FlagValue = string | boolean | numb ...

Formatting decimals with dots in Angular using the decimal pipe

When using the Angular(4) decimal pipe, I noticed that dots are shown with numbers that have more than 4 digits. However, when the number has exactly 4 digits, the dot is not displayed. For example: <td>USD {{amount| number: '1.2-2'}} < ...

Collaborate on sharing CSS and TypeScript code between multiple projects to

I am looking for a solution to efficiently share CSS and TS code across multiple Angular projects. Simply copy-pasting the code is not an ideal option. Is there a better way to achieve this? ...

Using a memory cache in development with NextJS does not seem to be effective

When exporting my pages for my simple static blog site, everything runs smoothly and quickly. However, in development mode, the generation of posts is sluggish and I'm looking to implement caching to speed up the process. I have set up a file called p ...

Question about TypeScript annotations: arrays containing key-value pairs

Is there an explanation for why this issue occurs in VSCode? interface Point { x: number; y: number; } let grid: [key: number, value: [key: number, value: Point]]; // ... // Accessing an object of type number | [key: number, value: Point] var c ...

Dynamically pass a template to a child component

How can I dynamically load content on my page based on the active navigation point? export class Sub_navigation_item { constructor( public title: string, public templateName: string ) {} } I have a navigation item with an ID from an ...

What is the best way to eliminate the final character in an input value once it has passed through regex validation in Angular8

Hello! I am attempting to remove the last digit of a string and update the input value each time my function checks if the input value passes a regex test on keypress. Initially, it works correctly, but then it adds an extra digit. After that, it works a ...

Are there alternative methods for incorporating react-multi-carousel in a project utilizing NEXT.js and React/Typescript?

I installed the react-multi-carousel module and configured a simple carousel, but it's not functioning properly. The issue seems to be related to the absence of <li> tags. Here is the HTML that was rendered: <ul class="react-multi-caro ...

Securely import TypeScript modules from file paths that are dynamically determined during execution

Imagine you have a structure of TypeScript code and assets stored at a specific URL, like between a CDN and a debug location. You want to import the main module and ensure the rest of the structure is imported correctly only when needed, without repeating ...

The combination of Stripe, Angular, and TypeScript is not compatible

Attempting to utilize Stripe.card.createToken() in order to generate a token for backend usage has proven to be challenging. Integrating this functionality with Angular and TypeScript requires careful coordination. Currently, the angular-stripe and stripe. ...

Styling <Link> component with styled-components: A step-by-step guide

Utilizing the Link component from @material-ui/core/Link in my TypeScript code was initially successful: <Link href="#" variant="body2"> Forgot? </Link> However, I am exploring the transition to styled-components located in a separate file. ...

Experiencing an issue with type error when transitioning syntax from using require to import

After transitioning from require in CommonJS syntax to import in ES Module syntax, I encountered an error. I decided to develop a todo-app using Node.js, TypeScript, and MySQL. To start off, I wrote the initial code snippets below: // db.ts export {}; co ...

Angular - Using HttpClient for handling POST requests

The example provided in the official Angular HttpClient documentation demonstrates how to make a POST request to a backend server. /** POST: add a new hero to the database */ addHero (hero: Hero): Observable<Hero> { return this.http.post<Hero&g ...

What is the process for importing a file with an .mts extension in a CJS-first project?

Here's a snippet from a fetchin.mts file: import type { RequestInfo, RequestInit, Response } from "node-fetch"; const importDynamic = new Function("modulePath", "return import(modulePath);") export async function fetch(u ...

Setting up Electron to utilize TypeScript's baseUrl can be achieved by following a few simple steps

In TypeScript, there is a compiler option known as baseUrl that allows you to use non-relative paths, like: import Command from "util/Command" as opposed to: import Command from "../../../util/Command" While this works fine during compilation, TypeScri ...