javascripttypescriptinheritancedecoratortypescript-decorator

Unable to access base class methods in derived class when using method decorators in TypeScript


Can someone help me understand following things as when I am using method decorator in TS -

I have added inline comments in code to highlight parts I am referring to, any help is much appreciated, thanks in advance!

(TS playground if someone want's to have a look at trans-compiled code, play around and see the error for themselves).

Code

const decorator = (val: string) => {
  return function (t: any, pk: any, d: any) {
    console.log("decorator val " + val)
    const ow = d.value
    d.value = (props: any) => {
      console.log({t, d})
      console.log("decorator ", { props })
      return ow.call(t, props)
    }
  }
}

abstract class Base {
  protected val: string
  protected abstract abstractFn(val: string): void

  protected constructor(val: string) {
    this.val = val
  }

  protected base = (val: string) => {
    console.log("base " + val)
  }

  protected nonArrowFunctionBase(val: string) {
    console.log("nonArrowFunctionBase " + val)
  }
}

class Derived extends Base {
  public constructor(val: string = 'default') {
    super(val)
  }

  @decorator("world")
  public abstractFn(val: string) {
    // this.val is returning undefined even though it is initialized, why?
    console.log('derived:abstractFn ' + val + ' baseval ' + this.val) 

    // This call is working, why?
    this.nonArrowFunctionBase(val)

    // This call is not working, why?
    this.base(val) 
  }
}

const test = new Derived()
test.abstractFn('Test')

Output

[LOG]: "decorator val world" 
[LOG]: {
  "t": {},
  "d": {
    "writable": true,
    "enumerable": false,
    "configurable": true
  }
} 
[LOG]: "decorator ",  {
  "props": "Test"
} 
[LOG]: "derived:abstractFn Test baseval undefined" // Why `this.val` is undefined?
[LOG]: "nonArrowFunctionBase Test" // Why non arrow function is callable?
[ERR]: this.base is not a function  // Why `this.base(val)` call fails?

I want to understand when using method decorators, why we're not able access Base class methods/values in Derived class? And how we can make it work with decorators?


Solution

  • You are using an arrow function where you need a proper function:

    let a = {
      foo(){ console.log('a.foo', {this: this}) /* {this: a} */ }
    }
    let b = {
      foo: function() { console.log('b.foo', {this: this}) /* {this: b} */ }
    }
    let c = {
      foo: () => { console.log('c.foo', {this: this}) /* {this: globalThis} */ }
    }
    

    Remember, arrow functions don't have their own this

    And the second thing: t is not this, it's some other object.
    You need an actual this there:

    const decorator = (val: string) => {
      return function (t: any, pk: any, d: any) {
        console.log("decorator val " + val)
        const ow = d.value
        d.value = function(props: any) {
          //      ^^^^^^^^
          console.log({t, d})
          console.log("decorator ", { props })
          return ow.call(this, props)
          //             ^^^^
        }
      }
    }