typescripttypescript-decorator

Typescript decorator confusion


I've been playing with TypeScript decorators but just having no luck getting them working. I read How to implement a typescript decorator? and also http://blog.wolksoftware.com/decorators-reflection-javascript-typescript from which I created the decorator implementation

function log(target: any, key: string, descriptor?: any) {

  // save a reference to the original method
  // this way we keep the values currently in the 
  // descriptor and don't overwrite what another 
  // decorator might have done to the descriptor.
  if(descriptor === undefined) {
      descriptor = Object.getOwnPropertyDescriptor(target, key);
  }
  var originalMethod = descriptor.value; 

  //editing the descriptor/value parameter
  descriptor.value =  function (...args: any[]) {
      var a = args.map(a => JSON.stringify(a)).join();
      // note usage of originalMethod here
      var result = 12;//originalMethod.apply(this, args);
      var r = JSON.stringify(result);
      console.log(`Call: ${key}(${a}) => ${r}`);
      return result;
  }

  // return edited descriptor as opposed to overwriting 
  // the descriptor by returning a new descriptor
  return descriptor;
}

class C {
    constructor(){
        console.log("built");
    }
    @log
    public foo(n: number) {
        console.log("foo called");
        return n * 2;
    }
}
//new MethodDecoratorExample().method("hi");
var c = new C();
var r = c.foo(23); //  "Call: foo(23) => 12"
console.log(r);

However running this code doesn't yield what I expect, in fact it doesn't seem to override the descriptor. Digging in more I find that in the generated code the call to __decorate has only three parameters

__decorate([
    log
], C.prototype, "foo");

This means that in __decorate c = 3 as the descriptor is not passed in. As a result the new descriptor isn't returned and the code flow progresses as if the method hasn't been intercepted.

So I must be applying this decorator incorrectly but I can't see how I'm doing it wrong. Is it perhaps being interpreted as a property descriptor? I did see some comment about that somewhere but that was in relation to defining the foo method in C as a lambda which I'm not doing.

How can I get this working?


Solution

  • The issue here is that TypeScript will generate ES3 code by default, and method decorators generated for ES3 apparently work somewhat differently than the ones generated for ES5. So, unless you actually need to target ES3 the simple way to fix your problem is to target ES5 with tsc --experimentalDecorators --target es5, or ES6 with tsc --experimentalDecorators --target es6.