-1

According to https://github.com/microsoft/TypeScript/issues/6331 referencing this in static is perfectly legal, however, using a class like:

class ZController {
    static async b(req: RequestType, res: Response) {
            await this.a(req);
    }

    static async a(req) {
        console.log('here')
    }
}

results in:

Error: unhandledRejection: Cannot read properties of undefined (reading 'a')
TypeError: Cannot read properties of undefined (reading 'a')
    at b (/usr/src/app/controllers/z.ts:24:33)
    at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)
    at next (/usr/src/app/node_modules/express/lib/router/route.js:137:13)
    at xxx (/usr/src/app/middlewares/Auth.js:108:17)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

I am running Typescript 4.4.2.

Why is this? According to my research typescript should support this.

Sammaye
  • 43,242
  • 7
  • 104
  • 146
  • Please provide an [mcve]. How you *call* a function often effects the value of `this` and you haven't shown that. – Quentin Jul 18 '22 at 10:56
  • @Quentin Hmm that is quite simple, it shows me calling form one static to another, all I have done is trimmed return and a variable assignment, I am not doing anything special, this is the object's this in native JS – Sammaye Jul 18 '22 at 11:10
  • The value of `this` inside `b` depends on how you call `b` and you have not shown us how you call `b`. – Quentin Jul 18 '22 at 11:12
  • @Quentin From the stacktrace, it is apparent that `b` is passed, unbound, to the express router. – spender Jul 18 '22 at 11:20
  • @Quentin not really unless you actually bind this, in JS everything is an object, as such if you call this you actually get a this of the current object, in this case the static global object you just created, likewise in instances this relates to the copy of that static object – Sammaye Jul 18 '22 at 11:28
  • Trust me, try this in native nodejs, you don't need to bind anything – Sammaye Jul 18 '22 at 11:29

1 Answers1

-1

Somewhere in your code your are passing an unbound copy of ZController.b. This means that when you call it, this will not be bound to ZController.

type Req = {};
type Res = {}
class ZController {
    static async b(req: Req, res: Res) {
        await this.a(req);
    }

    static async a(req: Req) {
        console.log('here')
    }
}

ZController.b({}, {}); // works fine

const zb = ZController.b; // zb is not bound to `ZController`

zb({}, {}); // so here, we see failure... this is undefined

//fix by binding back to ZController

const zb2 = ZController.b.bind(ZController);

zb2({}, {}); // works fine

// or wrapping:

const zb3 = (req: Req, res: Res) => ZController.b(req, res);

zb3({}, {}); // works fine

or just don't use this in your static methods

class ZController2 {
    static async b(req: Req, res: Res) {
        await ZController2.a(req);
    }

    static async a(req: Req) {
        console.log('here2')
    }
}

const zb4 = ZController2.b;

zb4({},{}) //works

Playground Link

spender
  • 117,338
  • 33
  • 229
  • 351
  • So unlike what the link says typescript does not have legal this in static context? I have to bind it? – Sammaye Jul 18 '22 at 10:58
  • @Sammaye It is "legal". If you call a function on an object with dot notation, then then function's `this` is bound. However, if you **take a "free-standing" copy of it** `const zb = ZController.b` and call it, then the binding is lost. You can fix it by binding the free-standing function to `ZController`, or (when you reference it), wrapping it: `(req: Req, res: Res) => ZController.b(req, res)`. – spender Jul 18 '22 at 11:02
  • Is ther a way of binding without doing it every single function? – Sammaye Jul 18 '22 at 11:07
  • @Sammaye That would be a different question that is (for instance) answered [here](https://stackoverflow.com/questions/56503531/what-is-a-good-way-to-automatically-bind-js-class-methods) or [here](https://stackoverflow.com/questions/30649398/automatically-call-bind-on-all-instance-methods-in-es6-during-constructor) – spender Jul 18 '22 at 11:10
  • @Sammaye Alternatively, don't use `this` in your static methods, and use `ZController` in its place. – spender Jul 18 '22 at 11:14
  • Yes, I think I will revert to using the class name in the static function, too bad ts clearly doesn't support this – Sammaye Jul 18 '22 at 11:30