简体   繁体   中英

Typescript method is return undefined with method decorator

I apologize if it is a silly question. I am new with typescript and learning typescript decorators. I found a code: MethodDecorator to log the arguments and the result.

log decorator

function log (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
    let originalMethod = descriptor.value;
    descriptor.value = function (...args:any[]) {
        //before
        console.log(`${key} method called with args: ${JSON.stringify(args)}`);

        let result = originalMethod.apply(this, args);

        //after
        console.log(`${key} method return value: ${JSON.stringify(result)}`);
    }
    return descriptor;
}

I am using @log with setId and getId methods in Book class

book.ts

class Book{
    constructor(public id: number, public title: string, public publisher: string){}

    @log
    setId(id: number){
        this.id = id;
    }
    @log
    getId(): number{
        return this.id;
    }
}

All code works fine but getId returns undefined when i run this code.

let book = new Book(1, "Learn TypeScript", "O\'Reilly Media");

let favBookId = book.getId();
console.log("Book before setId: ");
console.log(book);
console.log("Favourite book id: "+favBookId);

book.setId(5);
console.log("Book after setId: ");
console.log(book);
console.log("Favourite book id: "+favBookId);

my tsconfig.json

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "removeComments": false,
        "experimentalDecorators": true
    }
}

To compile and run:

tsc -p ./
node book.js

Output:

getId method called with args: []
getId method return value: 1
Book before setId:
Book {id: 1, title: 'Learn TypeScript', publisher: 'O\'Reilly Media' }
Favourite book id: undefined
setId method called with args: [5]
setId method return value: undefined
Book after setId:
Book {id: 5, title: 'Learn TypeScript', publisher: 'O\'Reilly Media' }
Favourite book id: undefined

I am not able to understand why setId is working as I want and getId is not??

tsc -v: Version 1.8.10

I don't know why all the posts on the internet reference such examples, but I had problems of my own with the method decorator context regarding originalMethod.apply(this, args);

This indeed does NOT work AT ALL (I don't know if its a ts bug or something though).

After trial an error, I found that the following solution worked, allowing you to have the correct context inside your base class:

function myDecorator(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {

        descriptor.value = function (...args:any[]) {
            // whatever code suits you here...
            // dont use "this", use "target"
            let result = originalMethod.apply(target, args);

        }
        return descriptor;
}

Now this allows you to do:

export class Yeah {
    @myDecorator()
    helloWord() {
        let n = 5;
        return this.multiply(n);
    }

    multiply(a: number) {
       return a * 2;
    }

}

Cheers

OMG, I thought your problem is the same with me but it's not.

The problem is just caused by that you return nothing when re-define the function.

Here is your original code block:

descriptor.value = function (...args:any[]) {
        //before
        console.log(`${key} method called with args: ${JSON.stringify(args)}`);

        let result = originalMethod.apply(this, args);

        //after
        console.log(`${key} method return value: ${JSON.stringify(result)}`);
    }

Now, return it and it will work correctly.

descriptor.value = function (...args:any[]) {
        //...
        let result = originalMethod.apply(this, args);
        //...
        return result; //ADD HERE!!!
    }

This is a full working example:

function log(msg: string): MethodDecorator {
  return (
    target: Object,
    key: string | symbol,
    descriptor: PropertyDescriptor
  ) => {
    let originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
      //before
      if (args.length >0)  console.log(`${msg} ${key.toString()} method called with args: ${JSON.stringify(args)}`);
      let result = originalMethod!.apply(this, args);
      //after
      if (result) console.log(`${key.toString()} method return value: ${JSON.stringify(result)}`);
    };
    return descriptor;
  };
}

class Book {
  constructor(
    public id: number,
    public title: string,
    public publisher: string
  ) {}

  @log('setting ')
  setId(id: number) {
    this.id = id;
  }
  @log('getting ')
  getId(): number {
    return this.id;
  }
}

const a = new Book(1, "title", "publisher");
a.setId(2);
a.getId();

You can run it at typescript playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM