1

I have a class Chain with a method syncPassRequest in which I used the apply() method to set a class instance as another function's (this.fn) this. When I tried console.log(this) in a function which will be passed to the class instance, I found there is an error

"An outer value of 'this' is shadowed by this container."

I was expecting to get this as the class instance, because I used apply to pass it.

Why I want to do this? It's because I want to get another method asyncPassRequest from the class instance to meet the async need.

(This was in a practice of chain of responsibility)

This is the class declaration

type ChainFunc = (...args: any) => any;

export class Chain {
  public fn: ChainFunc;
  public successor: Chain | null = null;
  constructor(fn: ChainFunc) {
    this.fn = fn;
  }
  public setSuccessor(successor: Chain | null): any {
    return (this.successor = successor);
  }
  public syncPassRequest(...args: any): any {
   // here, i use apply to bind class Chain's instance to fn's this
    const res = this.fn.apply(this, args);
    console.log(this.fn.name, res);
    if (res === "nextSuccessor") {
      return (
        this.successor &&
        this.successor.syncPassRequest.apply(this.successor, args)
      );
    }
    return res;
  }
  public asyncPassRequest(...args: any) {
    this.successor &&
      this.successor.syncPassRequest.apply(this.successor, args);
  }
}

this is the function declaration

import { Chain } from "./chain_of_responsibility";

function fn1(msg: string) {
  if (msg === "pass") {
    console.log("fn1 pass");
    return "nextSuccessor";
  }
  console.log("fn1 stop");
  return "stop";
}

function fn2(msg: string) {
  setTimeout(() => {
   // here, i wanted to console.log this, i was expecting the class Chain's instance
   // but it gave me an error
    console.log(this);
   // i want to use this.asyncPassRequest to meet the async need
    this.asyncPassRequest();
  }, 1000);
}

function fn3(msg: string) {
  if (msg === "pass") {
    console.log("fn3 pass");
    return "nextSuccessor";
  }
  console.log("fn3 stop");
  return "stop";
}

const c1 = new Chain(fn1);
const c2 = new Chain(fn2);
const c3 = new Chain(fn3);

c1.setSuccessor(c2);
c2.setSuccessor(c3);

c1.syncPassRequest("pass");

I tried to use arrow function to declare the function, but I failed, because arrow function is cannot have their this value changed with apply(), bind(), or call().

This is the error I got

dh@localhost chain_of_responsbility % ts-node test.ts
/Users/dh/.nvm/versions/node/v12.15.0/lib/node_modules/ts-node/src/index.ts:859
    return new TSError(diagnosticText, diagnosticCodes, diagnostics);
           ^
TSError: ⨯ Unable to compile TypeScript:
test.ts:14:17 - error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.

14     console.log(this);
                   ~~~~

  test.ts:12:10
    12 function fn2(msg: string) {
                ~~~
    An outer value of 'this' is shadowed by this container.
test.ts:15:5 - error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.

15     this.asyncPassRequest();
       ~~~~

  test.ts:12:10
    12 function fn2(msg: string) {
                ~~~
    An outer value of 'this' is shadowed by this container.

    at createTSError (/Users/dh/.nvm/versions/node/v12.15.0/lib/node_modules/ts-node/src/index.ts:859:12)
    at reportTSError (/Users/dh/.nvm/versions/node/v12.15.0/lib/node_modules/ts-node/src/index.ts:863:19)
    at getOutput (/Users/dh/.nvm/versions/node/v12.15.0/lib/node_modules/ts-node/src/index.ts:1077:36)
    at Object.compile (/Users/dh/.nvm/versions/node/v12.15.0/lib/node_modules/ts-node/src/index.ts:1433:41)
    at Module.m._compile (/Users/dh/.nvm/versions/node/v12.15.0/lib/node_modules/ts-node/src/index.ts:1617:30)
    at Module._extensions..js (node:internal/modules/cjs/loader:1272:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/dh/.nvm/versions/node/v12.15.0/lib/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1081:32)
    at Function.Module._load (node:internal/modules/cjs/loader:922:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) {
  diagnosticCodes: [ 2683, 2683 ]
}
VLAZ
  • 26,331
  • 9
  • 49
  • 67
nofx3000
  • 11
  • 2
  • I'm not even sure why you use `this.successor.syncPassRequest.apply(this.successor, args)` instead of the simpler `this.successor.syncPassRequest(...args)` – VLAZ Mar 21 '23 at 08:36
  • With the added code, it's just a type annotation thing. You need to declare `function fn2(this: Chain, msg: string)` to specify the type of `this`. [Demo](https://tsplay.dev/WzGYLw). Note that using `.apply()` to call the method is a red herring here. You do not need it: [Demo without `.apply()` to call the class method](https://tsplay.dev/mxjGBw) – VLAZ Mar 21 '23 at 09:00
  • Thanks mate, problem solved with using function fn2(this: Chain, msg: string), referring https://www.typescriptlang.org/docs/handbook/2/classes.html#this-parameters – nofx3000 Mar 22 '23 at 00:20

0 Answers0