Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript method decorator -- `this` with noImplicitThis enabled

I'm trying to write a simple method decorator, that does some simple logic before calling the original method. All the examples I could find boil down to calling originalMethod.apply(this, args) at the end -- but with noImplicitThis enabled in tsconfig.json, I get the following error:

[eval].ts(1,224): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.

I tried working around by calling originalMethod.apply(this as any, args), but the error persists.

Question: Are there any workarounds to call the original method, without disabling noImplicitThis for the whole project?


Minimal example -- works with noImplicitAny disabled:

function logBeforeCall1(): originalMethodDecorator {
    return function(
        target: any,
        propertyKey: string | symbol,
        descriptor: PropertyDescriptor,
    ): PropertyDescriptor {
        const originalMethod = descriptor.value;
        descriptor.value = (...args: any[]) => {
            console.log('hello!');
            return originalMethod.apply(this, args);
        };
        return descriptor;
    };
}

class Test1 {
    private num: number = 1;

    @logBeforeCall1()
    test(next: number): void {
        console.log(this.num, '->', next);
        this.num = next;
    }
}

  • I already know that decorators don't have access to a specific object instance (see e.g. this question), but using this in the above example works
  • Event the official docs on decorators use the construction from my example (see the Property Decorators section), so it works, but conflicts with noImplicitThis compiler option...
like image 258
quezak Avatar asked Dec 18 '18 16:12

quezak


2 Answers

Thanks to @Grassator for inspiration -- this is a version that works. I don't believe this can be done type-safely in the current state of TypeScript though.

function logBeforeCall2(): originalMethodDecorator {
    return function(
        target: any,
        propertyKey: string | symbol,
        descriptor: PropertyDescriptor,
    ): PropertyDescriptor {
        const originalMethod = descriptor.value;
        descriptor.value = function(this: any, ...args: any[]): any {
            console.log('hello!');
            return originalMethod.apply(this, args);
        };
        return descriptor;
    };
}

...

> t2.test(5)
hello!
1 '->' 5
like image 54
quezak Avatar answered Nov 15 '22 06:11

quezak


I do not think you can really get type safety, but if you just want to get rid of type error you can explicitly say that this: any (3rd line):

function logBeforeCall1() {
  return function (
    this: any,
    target: any,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor,
  ): PropertyDescriptor {
    const originalMethod = descriptor.value;
    descriptor.value = (...args: any[]) => {
      console.log('hello!');
      return originalMethod.apply(this, args);
    };
    return descriptor;
  };
}
like image 27
Dmitriy Avatar answered Nov 15 '22 05:11

Dmitriy