I have a typescript decorator factory which console logs total time taken to execute a function, actual function execution results and parameters passed to the decorator as well.
e.g.
export function performaceLog(...args: any[]) {
return (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) => {
var msg = '';
if (args.length != 0) {
msg = args.map(arg => arg).join(' ');
}
if (descriptor === undefined) {
descriptor = Object.getOwnPropertyDescriptor(target, key);
}
if (typeof descriptor.value !== 'function') {
throw new SyntaxError('Only functions can be used with log decorators');
}
var originalMethod = descriptor.value.bind(target);
descriptor.value = function() {
var funcArgs: any = [];
for (var i = 0; i < arguments.length; i++) {
funcArgs[i - 0] = arguments[i];
}
var startTime = performance.now();
console.log(`Begin function ${key} with params (${funcArgs}) : ${msg}`);
var result = originalMethod.apply(this, funcArgs);
var endTime = performance.now();
console.log(`End function ${key}. Execution time: ${endTime - startTime} milliseconds. Return result : ${result}`);
return result;
};
return descriptor;
};
}
I am using the above decorator with a function which is present in a class: (considering my class has other methods as well like ctor, get, set and utils).
class LoggerService {
@performaceLog('validate', 'message')
handleMessage(message) {
console.log(message);
return true;
};
}
Function call looks like this:
handleMessage('decoratorValidation');
This gives me perfect output as:
Begin function handleMessage with params (decoratorValidation) : validate message
decoratorValidation
End function handleMessage. Execution time: 0.3000000142492354 milliseconds.
Return result : true
But when I change the function handleMessage to support arrow format (ES6), it throws me an error:
@performaceLog('validate', 'message')
handleMessage = message => {
console.log(message);
return true;
};
Error Message:
Unable to resolve signature of property decorator when called as an expression.
I have all the parameters set correctly in my tsconfig.json. I have my entire project supporting "ES6" target and I want the decorator to support arrow functions.
I have a typescript decorator factory which console logs total time taken to execute a function, actual function execution results and parameters passed to the decorator as well.
e.g.
export function performaceLog(...args: any[]) {
return (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) => {
var msg = '';
if (args.length != 0) {
msg = args.map(arg => arg).join(' ');
}
if (descriptor === undefined) {
descriptor = Object.getOwnPropertyDescriptor(target, key);
}
if (typeof descriptor.value !== 'function') {
throw new SyntaxError('Only functions can be used with log decorators');
}
var originalMethod = descriptor.value.bind(target);
descriptor.value = function() {
var funcArgs: any = [];
for (var i = 0; i < arguments.length; i++) {
funcArgs[i - 0] = arguments[i];
}
var startTime = performance.now();
console.log(`Begin function ${key} with params (${funcArgs}) : ${msg}`);
var result = originalMethod.apply(this, funcArgs);
var endTime = performance.now();
console.log(`End function ${key}. Execution time: ${endTime - startTime} milliseconds. Return result : ${result}`);
return result;
};
return descriptor;
};
}
I am using the above decorator with a function which is present in a class: (considering my class has other methods as well like ctor, get, set and utils).
class LoggerService {
@performaceLog('validate', 'message')
handleMessage(message) {
console.log(message);
return true;
};
}
Function call looks like this:
handleMessage('decoratorValidation');
This gives me perfect output as:
Begin function handleMessage with params (decoratorValidation) : validate message
decoratorValidation
End function handleMessage. Execution time: 0.3000000142492354 milliseconds.
Return result : true
But when I change the function handleMessage to support arrow format (ES6), it throws me an error:
@performaceLog('validate', 'message')
handleMessage = message => {
console.log(message);
return true;
};
Error Message:
Unable to resolve signature of property decorator when called as an expression.
I have all the parameters set correctly in my tsconfig.json. I have my entire project supporting "ES6" target and I want the decorator to support arrow functions.
Share Improve this question edited Apr 19, 2018 at 20:07 Shreyas Patil asked Apr 19, 2018 at 18:53 Shreyas PatilShreyas Patil 631 silver badge8 bronze badges1 Answer
Reset to default 9performaceLog
is supposed to work with prototype methods only because it relies on descriptor
, which should be optional.
There is no descriptor
in case of handleMessage = message => ...
class field because it doesn't exist on class prototype. Class fields are just assigned to this
in constructor.
This line won't work for same reason:
descriptor = Object.getOwnPropertyDescriptor(target, key);
In order to patch arrow method in decorator, a trap should be set on class prototype. Here is an example of universal decorator that can be used with both prototype and instance methods; it uses get
/set
to catch proper this
context and cache decorated function to patchFn
variable. It returns a descriptor regardless of descriptor
parameter:
function universalMethodDecorator(target: any, prop: string, descriptor?: TypedPropertyDescriptor<any>): any {
let fn;
let patchedFn;
if (descriptor) {
fn = descriptor.value;
}
return {
configurable: true,
enumerable: false,
get() {
if (!patchedFn) {
patchedFn = (...args) => fn.call(this, ...args);
}
return patchedFn;
},
set(newFn) {
patchedFn = undefined;
fn = newFn;
}
};
}
This applies only to TypeScript decorators. Babel legacy decorators may behave differently.
As explained in this answer, prototype methods can be preferred to instance methods for several reasons. One of the reasons is that they can be seamlessly decorated because decorators are applied to class prototype. The only real benefit of arrow method is that it is naturally bound to class instance, but since the decorator is already in use, prototype method can be bound in decorator if needed (this is what universalMethodDecorator
basically does; it is noop for arrow methods).