What is the method for reaching a static member of a class within a decorator for a method of the same class?

Upon the release of TypeScript 5.0, the new decorator APIs have been introduced and I am eager to utilize them for automatically providing parameters to a method from a static property within the same class. The decorator parameters and factory are defined as follows:

type TargetFunction<This, Args extends unknown[], Return> = (this: This, ...args: Args) => Return;

class Throwable extends Error {} // For demonstration purposes.

export interface ITestParameters {
    dataProvider?: string;
    expectedExceptions?: typeof Throwable;
}

export const Test = <T extends ITestParameters>(param: T) => {
    return <This, Args extends unknown[], Return>(target: TargetFunction<This, Args, Return>,
        context: ClassMethodDecoratorContext<This, TargetFunction<This, Args, Return>>) => {
        return function (this: This, ...args: Args): Return {
            if (param.dataProvider) {
                const newArgs = (this as any)[param.dataProvider] as Args;
                return target.call(this, ...newArgs) as Return;
            }

            return target.call(this, ...args) as Return;
        };
    };
};

To apply this factory, it can be used in a test class like so:

class SubList {
    @Test({ dataProvider: "modifiable" })
    public testMod(list: number[], from: number, to: number): void {
        console.log(list, from, to);
    }

    public static modifiable: [number[], number, number] = [[1, 2, 3], 4, 5];
}

(new SubList()).testMod([], 0, 0); // error, newArgs is undefined

In the playground, it appears that while the code is functional, accessing the static property in the class mentioned as a factory parameter is not possible. What modifications are required to rectify this issue?

The use of the this parameter does not provide access to the static method. Attempts such as

this.prototype[param.dataProvider]
have also resulted in errors.

Answer №1

Whenever the decorated method is called, it receives a this context that points to an instance of the class being decorated. However, static properties in classes are associated with the class constructor itself and not with its instances. To access a static property from an instance, you must refer to it via the constructor property. Therefore, instead of using this[param.dataProvider], you should use

(this.constructor)[param.dataProvider]
.

In TypeScript, the constructor property of class instances is not strongly typed due to issues explained in microsoft/TypeScript#3841. To make the code compile successfully, you will need to employ type assertions. Since you were already using type assertions within the decorator function, this would be an additional one. For the sake of avoiding errors, I'll assert that this is of type the any type:

const newArgs = (this as any).constructor[param.dataProvider] as Args;

Following these adjustments, when the code is executed, the decorated method will receive its arguments from the static class property:

(new SubList()).testMod([], 0, 0); // [1, 2, 3], 4, 5

You can test the code on the playground by following this https://www.typescriptlang.org/play?#code/LAKALgngDgpgBAFQIYCcDmMwDECuA7AYzAEsB7PAHgQAtiBnAGjgEF064YAPMGPAE3b4A1nlIB3PAG0AukwBKmHCjwA+OAF44ACjC06ALkR6mAOjOo0BlmwCUGtQrBK8AblCgCAGyR12NFOJIAEae8Fw8-OwAoigBKHAA3nAAvnAA9GlwAPJ4nhBwAGak8XwwALbkdGAoSCTkJu4gxHg8KAVIBPAAkggwVQAKqEhlmDAo7AmgcNNwfLVI-QEAbsSlKAD8hlUozWhuIDMcnLBEMHxRnJ1QdXh0m3CQsKQFRgFiwaH7yY0ElWCIfX+mioRwiAjgPUBgxqI1adBUWigQzKhgQdnUakmBxmKEUyjgVGM1ksoN44OEogkMnkeNUOgsmFRDOw+CIZEoNHoTFYlhpTmUKgYU0OM1+LXChgAwt5fABZTDUUh8AAiMF+NTAxUl5B43EJXMQzNwhBu+sYxPNjmcKhU6MxwpFcFx-LwhVZN20unoqKJZhMFisPLoNkMVvxWMdjuIL0RyJMczAC2WqzGdgjkYzYqqcDwMDEQY0nr0cB8JbwEBsJiz1RwRGKkiRMPj80WpBWa2kJfYQf2GYzzucD2ZVaQnk8Ol9Zlz+dsXbgYdcDr73xAS-7tKH6EwI7HE4NfoDdlLC97keSp+m59AV9XIC8PnYAGUcEEADL0f7p6YAAV6VS0SQJkmbYpighgAEQVHw0bEB8MDgSkNhLlAL6eMQBAPICspKloaFVIYeA4GUQRjNShQBCiOZESRKBMJqBHUamhhLKQqyJGuoqVKQoQmJ4pBoLhH5MAUFF0aQNgXikjSHChIToXAVS1PJUEwXBhiSIRxGkbIVFabRuk0Z2miSJIACMTAAExMAAzDpAAsTAAKzSF8jRaNOcDPm+H5aDYlY8FU2F8FoZEAAxMKFEnpJkZmWTZ9lOUAA

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

Updated the application to Angular 6 but encountered errors when attempting to run npm run build --prod. However, running the command npm run build --env=prod was executed without any issues

Running the command npm run build -- --prod results in the following error messages: 'PropertyName1' is private and can only be accessed within the 'AppComponent' class 'PropertyName2' does not exist in type 'AppCompo ...

JavaScript heap exhausted while running Docker container

Typically, I start my application by running npm run dev. The package.json file contains a script like the one below: "scripts": { "dev": "nodemon server.ts", } Everything is working fine in this setup. I have cr ...

Typescript or Angular 2 for Google Maps

Is there a way to integrate Google Maps Javascript API with Typescript or Angular 2? Although libraries like https://github.com/SebastianM/angular2-google-maps are available, they may not provide full support for features like Events and Places Libraries ...

Exploring a Component's props and their data types

As a newcomer to React and Typescript, I have a straightforward question that I can't seem to find an answer to. I'm attempting to construct a tab layout using Typescript with headless UI following the documentation here I am encountering issue ...

Deduce the types of parameters in nested functions based on the parent object

Having encountered a challenging obstacle in my Typescript journey, I find myself facing a dilemma with building a command-parsing utility. My aim is to incorporate type hints within configuration objects. Let's delve into the code example below; int ...

Tips for setting up a listener for when the month changes in an ion-datetime object

When the user selects a different month, I need to update the highlightedDates by calling a query to retrieve all the dates of that specific month. This currently works if the user manually chooses a month and year using the top button, but not when they c ...

Executing the outer function from within the inner function of a different outer function

Imagine this scenario: function firstFunction() { console.log("This is the first function") } secondFunction() { thirdFunction() { //call firstFunction inside thirdFunction } } What is the way to invoke firstFunction from thirdFunction? ...

Loading an external javascript file dynamically within an Angular component

Currently, I'm in the process of developing an Angular application with Angular 4 and CLI. One of my challenges is integrating the SkyScanner search widget into a specific component. For reference, you can check out this Skyscanner Widget Example. T ...

We are in need of a provider for the Ionic Network native plugin

I have encountered an issue while trying to use Ionics native plugin "Network" as it fails due to a missing provider. To prevent any errors, I performed a fresh installation of Ionic along with the necessary dependencies: ionic cordova plugin add cordova- ...

HTMLElement addition assignment failing due to whitespace issues

My current challenge involves adding letters to a HTMLElement one by one, but I'm noticing that whitespace disappears in the process. Here's an example: let s = "f o o b a r"; let e = document.createElement('span'); for (let i ...

Decoding Dynamic JSON Data into a Map Using Angular

When working with Angular's http client to fetch data from an API, I create DTOs using typescript interfaces. One of the endpoints returns a JSON object that contains dynamic keys which I want to map into a Map. After trying out different approaches, ...

Is it impossible to extend a Typescript class with an overriding method that uses a different parameter?

I am currently working on a Typescript MVC app and encountering an issue. When I try to extend my BaseController and override the ajaxMethod with different parameters, my transpiler throws an error. Any help would be appreciated. Below is the code snippet ...

What is the recommended approach for testing a different branch of a return guard using jest?

My code testing tool, Jest, is indicating that I have only covered 50% of the branches in my return statement test. How can I go about testing the alternate branches? The code snippet below defines a function called getClient. It returns a collection h ...

Is it possible to pass a variable to a text constant in Angular?

In my constant file, I keep track of all global values. Here is the content of the file: module.exports = { PORT: process.env.PORT || 4000, SERVER: "http://localhost:4200", FAIL_RESULT: "NOK", SUCCESSFUL_RESULT: "OK ...

Unraveling the intricacies of the relationship between `extends` and function types in TypeScript

Example 1 seems clear to me type A = (1 | 2 | 3) extends (infer I) ? [I] : never; // A = [1 | 2 | 3] In Example 2, the type variables are being intersected but I'm unsure why type A = ( ((_: 1) => void) | ((_: 2) => void) | ((_: 3) => ...

What is the best way to send parameters through the router in Next14?

Is there a way to pass parameters through the router with NextJS 14? I'm developing an app that features multiple items, and when a user clicks on one, they should be taken to that item's individual page. I'd like the URL to display as http ...

retrieving JSON data within HTML elements

How can I access the JSON values {{u.login}} from HTML instead of just through JavaScript? Currently, I am only able to access them using JS. Is there a way to directly access JSON values in HTML? At the moment, I am getting them as text. Could you please ...

An uncaught security error occurred when attempting to execute the 'pushState' function on the 'History' object

Here are the routes in my application: const routes:Routes =[ {path:'', component:WelcomeComponent}, {path:'profile', component: ProfileComponent}, {path:'addcourse', component: AddcourseComponent}, {path:'course ...

How to efficiently display nested object data using Angular Keyvalue pipe

I am facing an issue with a particular HTTP request that returns an observable object containing multiple properties. One of these properties is the 'weight' object which has two key values, imperial and metric. While attempting to loop through ...

shortcut taken in inferring now exported

When using a default export, if the expression consists only of a variable identifier, the type inferencer defaults to the declaration of the variable instead of the actual type of the expression. Is this behavior intentional or is it a bug? // b.ts const ...