Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

decorators: "this" is undefined when accessed in descriptor.value

I am trying out decorators, I have written a decorator that basically returns a new function that does some `console.log.

This is what my decorator looks like:

function test(target, name, descriptor) {
    const original = descriptor.value;
    console.log("bbau");
    if (typeof original === 'function') {
        descriptor.value = function (...args) {
            console.log(`Arguments: ${args}`);
            try {
                console.log("executing");
                const result = original.apply(this, args);
                console.log("done");
                console.log(`Result: ${result}`);
                return result;
            } catch (e) {
                console.log(`Error: ${e}`);
                throw e;
            }
        }
    }
    return descriptor;
}

And this is how I am using it:

class TestController extends BaseController<//..> {
    // ... 
    @test
    testIt(req: Request, res: Response) : Response {
       this.sendResponse();
    }

    sendResponse(options: ISendResponseOptions, res: Response) : Response {
       // return response
    }
}

`` However, when executed an error is raised: Error: TypeError: Cannot read property 'sendResponse' of undefined.

Any thoughts about what it could be? Thanks!

like image 364
Sid Avatar asked Feb 03 '26 10:02

Sid


1 Answers

You should generally use an arrow function when you want to capture this from the context you declared the function in (or when this does not matter). In this case you really want this to be the object the function was called on so you should use a regular function :

const test = (target, name, descriptor) => {
    const original = descriptor.value;
    if (typeof original === 'function') {
          descriptor.value = function (...args) {
            console.log(`Arguments: ${args}`);
            try {
                console.log("executing");
                const result = original.apply(this, args);
                console.log("done");
                console.log(`Result: ${result}`);
                return result;
            } catch (e) {
                console.log(`Error: ${e}`);
                throw e;
            }
        }
    }
    return descriptor;
}

You can test it out in the playground

If you use this function as a parameter to another function you should also call bind to set this for the function (otherwise the caller will determine the value of this):

router.route("/").post(testController.testIt.bind(testController))
like image 59
Titian Cernicova-Dragomir Avatar answered Feb 04 '26 23:02

Titian Cernicova-Dragomir