Employ a class decorator to modify methods within a subclass

Is there a way to utilize class decorators in order to modify the methods of subclasses for the decorated class? This particular example showcases how to alter the class's own methods, but does not extend to its subclasses:

export function guardAllNonConstructorOwnMethodsAgainstBadArguments(
  constructor: Function
) {
  const badArguments = new Set(['', undefined, null]);
  const prototype = constructor.prototype;
  Object.getOwnPropertyNames(prototype)
    .filter(
      ownPropertyName => typeof prototype[ownPropertyName] === 'function' && ownPropertyName !== 'constructor'
    )
    .forEach(propertyName => {
      const nonConstructorOwnMethod = prototype[propertyName];
      prototype[propertyName] = function(...args: any[]) {
        const everyArgumentIsGood = args.every(arg => !badArguments.has(arg));
        if (everyArgumentIsGood) {
          return nonConstructorOwnMethod.bind(this)(...args);
        }
      };
    });
}

So, with this specific scenario in mind, how could one adapt this decorator to also safeguard the methods of the subclasses belonging to the class where this decorator is used against "bad arguments"?

Answer №1

Class decorators are invoked only once, during the class definition process. They do not affect subclasses directly. To decorate subclasses, you would need to individually apply the decorator to each subclass.

Currently, there are no universal triggers available to automatically execute code when a subclass extends an already decorated superclass. One workaround could involve having subclasses invoke your code when their first instance is created. This means that while

class Subclass extends Superclass {}
doesn't trigger any action, calling new Subclass() will activate the code, but subsequent instances won't. It may suffice for some scenarios. Here's one possible implementation:

const handleForThisClassAndForEachSubclass =
    (cb: ((ctor: new (...args: any) => void) => void)) =>
        (ctor: new (...args: any) => any) => {
            const registry = new Set<new (...args: any) => any>();
            const alreadyDecorated = Symbol();
            const {[ctor.name]: newCtor} = {
                [ctor.name]: class extends ctor {
                    constructor(...args: any) {
                        super(...args);
                        const thisCtor = this.constructor as new (...args: any) => any;
                        if (!registry.has(thisCtor)) {
                            cb(thisCtor);
                            registry.add(thisCtor);
                        }
                    }
                }
            };
            cb(newCtor);
            registry.add(newCtor);
            return newCtor;
        };

This creates a registry of constructors and calls the callback function just once for each encountered constructor. Overriding the superclass constructor ensures that actions are triggered when constructing subclasses. While the approach may seem complex, its functionality can be demonstrated as follows:

const show = (ctor: new (...args: any) => any) => {
    console.log("I'm customizing this", ctor);
}

console.log("before declaring superclass")  
@handleForThisClassAndForEachSubclass(show)
class Superclass {

} // I'm customizing this function Superclass()
new Superclass(); // no output

console.log("before declaring subclass")
class Subclass extends Superclass {
} // no output

console.log("before creating subclass instance")
new Subclass(); // I'm customizing this function Subclass()
console.log("before creating subclass instance")
new Subclass(); // no output

console.log("before declaring another subclass")
class Subclass2 extends Superclass {
} // no output

console.log("before creating another subclass instance")
new Subclass2(); // I'm customizing this function Subclass2()
console.log("before creating another subclass instance")
new Subclass2(); // no output

console.log("before subsubclass declaration")
class SubSubclass extends Subclass {
} // no output

console.log("before subsubclass creation")
new SubSubclass(); // I'm customizing this function SubSubclass2()
console.log("before subsubclass creation")
new SubSubclass(); // no output

The function show() demonstrates being called once for SuperClass at creation time and again for each subclass upon their initial instantiation. Depending on your specific requirements, substituting show with your intended function like

guardAllNonConstructorOwnMethodsAgainstBadArguments
might yield useful results. Regardless, this method of overriding superclass constructors can lead to customized actions when constructing subclass instances. Best of luck with your application!

Link to code

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

Can you provide guidance on integrating TypeScript with React version 15.5?

I'm looking for the best approach to integrate TypeScript and React following the separation of PropTypes into a separate project with version 15.5. After upgrading from 15.4 to 15.5, everything seems to be running smoothly except for a warning in th ...

Experimenting with nested dual dynamic routing within the app's directory

Currently working with NextJS 13 and executing the following operations within the app directory. I am attempting to utilize the generateStaticParams function to generate static pages during build time. The route structure is: subpage/[categoryName]/[gif ...

What is the best way to specify types for a collection of objects that all inherit from a common class?

I am new to typescript and may be asking a beginner question. In my scenario, I have an array containing objects that all extend the same class. Here is an example: class Body{ // implementation } class Rectangle extends Body{ // implementation } class ...

Tips for integrating Reactjs with Chessboard.js

Recently, I stumbled upon chessboardjs (https://chessboardjs.com/) as a way to hone my React coding skills. However, I hit a roadblock while trying to implement a simple example of displaying the chess board in my application. The documentation instructed ...

The parameter type (key: string, id: string, pagination: number) in the argument does not match the expected type of Boolean for the parameter

I'm facing an issue while trying to implement the following documentation: https://swr.vercel.app/ using my own setup: import React, { useEffect } from 'react' import PatientsTable from 'components/patients/PatientsTable' import ...

Errors may occur when utilizing TypeScript with the Context Provider API in React

I am in the process of developing a theme changer feature and I want to save the color chosen by the user in the context. However, when I try to pass data to the value prop of the Context.Provider, I encounter an error message TS2739: Type 'Readonly&l ...

Exploring the method of including a mat-chip-list within a form

Can't submit form with mat-chip-list elements, even though they are present. How to send the tag array? Please assist! View my form here Here is the code I have so far: <mat-form-field class="example-chip-list"> <mat-chip-list #c ...

What could have caused the sudden halt of fetching on all server branches in the backend?

Following a code refactor on a separate branch, the fetch function ceases to work in any branch despite everything else functioning correctly. The error message reads: ...server/KE/utils.ts:44 const response = await fetch( ^ ReferenceError ...

Create a rectangle on the canvas using the Fabric.js library in an Angular application

I am attempting to create a rectangle inside a canvas with a mouse click event, but I am encountering some issues. The canvas.on('selection:created') event is not firing as expected. Below is my code: let can = new fabric.Canvas('fabricCanv ...

Encountered 'DatePickerProps<unknown>' error while attempting to develop a custom component using Material-UI and react-hook-form

Currently, I'm attempting to create a reusable component using MUI Datepicker and React Hook Form However, the parent component is throwing an error Type '{ control: Control<FieldValues, object>; name: string; }' is missing the follow ...

Error: Model attribute missing in Adonis JS v5 relationship

Recently, I started diving into the Adonis framework (v5) and decided to build a todo list api as part of my learning process. However, I'm facing an issue concerning the relationship between the User and Todo entities. Let me show you the models fo ...

The function you are trying to call is not valid... the specified type does not have any call signatures [ts 2349

Having some trouble using functions from Observable Plot example with a marimekko chart in my TypeScript project. I encountered an error on this particular line: setXz(I.map((i) => sum.get(X[i]))) The code snippet causing the issue is as follows: fu ...

Using masonry-layout with Next Js leads to a ReferenceError stating that window is not defined

Implementing the masonry-layout library by David Desandro in my Next app has been a smooth process. You can find the link here. When I apply it, the masonry layout functions perfectly as intended. Here's how I'm incorporating it successfully: imp ...

TypeScript introduces a flexible generic type, Optional<T, Props>, allowing customized props for a specific

In my attempt to develop a type called Optional<T, TProps>, where T represents the initial type and TProps is a union type of properties that need to be optional. As an illustration: type A = Optional<{a: string, b: string}, 'a'&g ...

In React-Typescript, the second index of the todos array is constantly being updated while the rest of the array remains unchanged

I am struggling to get my Todo-List working with typescript-react. The code I have doesn't seem to be functioning properly. Here is a snippet of my App.tsx: import { useState } from "react"; import "./App.css"; export default fun ...

Determining the data type of a generic variable within an Angular component

I'm currently in the process of developing a versatile component that can handle data of only two specific types: interface X{ name: string, path: string, type: string, } interface Y{ name: string, path: string, } Both types X a ...

The ListItemButton's onclick event does not trigger on the initial click when utilizing a custom component as its children

I am having trouble comprehending why this onclick function is influenced by the children and how it operates <ListItemButton onClick={() => onClickResult(q)}> <Typography variant="body1">{highlighted}</Typography> ...

Executing a Parent Function from a Child Component in Angular 11

I have successfully passed values between Angular components using Input/Output several times before, but I am currently facing a unique situation for which I cannot find a solution: There are two components involved: home.component (parent component) T ...

React is struggling to locate the specified module

Once I've set up my react project using npx create-react-app called my-app, I proceed to run npm start only to encounter the error shown in the image below. Running node version: 12.16.1 with npm version: 6.13.4 View the error message her ...

Connecting RxJS Observables with HTTP requests in Angular 2 using TypeScript

Currently on the journey of teaching myself Angular2 and TypeScript after enjoying 4 years of working with AngularJS 1.*. It's been challenging, but I know that breakthrough moment is just around the corner. In my practice app, I've created a ser ...