Reputation: 2040
I have the following class:
export abstract class CanDeactivateComponent {
abstract canLeavePage(): boolean;
abstract onPageLeave(): void;
@someDecorator
abstract canDeactivateBeforeUnload(): boolean;
}
and I get the error A decorator can only decorate a method implementation, not an overload
. I understand I can not put a decorator in that case but, which workaround can be applied so that I force all implementations of this class to use @someDecorator
before canDeactivateBeforeUnload
? Isn't there any way to put this decorator in the abstract class itself so that I don't have to write it in all the implementations?
Thank you!
Upvotes: 3
Views: 4105
Reputation: 860
How about method swizzling?
Method swizzling is a powerful technique in object-oriented programming that allows us to modify or extend the behavior of a method by intercepting its original implementation and replacing it with a different implementation. It is particularly useful when we want to add additional functionality, logging, or pre/post-processing to existing methods without modifying the original source code.
In this TypeScript example, we'll demonstrate method swizzling by intercepting the doSomething method of an abstract parent class and adding an additional action before calling the original implementation:
abstract class AbstractClass {
abstract doSomething(): void;
constructor() {
const originalDoSomething = this.doSomething.bind(this);
this.doSomething = function() {
console.log('Performing additional action before doSomething');
// Additional action logic here
// Call the original implementation
return originalDoSomething();
};
}
}
class MyClass extends AbstractClass {
doSomething() {
console.log('Original doSomething implementation');
// Original doSomething logic here
}
}
const myInstance = new MyClass();
myInstance.doSomething();
In this code snippet, the AbstractClass defines an abstract method doSomething. The method swizzling occurs within the constructor of AbstractClass. We store the original implementation of doSomething in originalDoSomething and then assign a new wrapper function to this.doSomething, which performs the additional action before calling the original implementation.
The MyClass class extends AbstractClass and provides its own implementation of the doSomething method.
Finally, we create an instance of MyClass and invoke the doSomething method. As a result, the swizzling logic defined in the AbstractClass constructor is applied, and both the additional action and the original implementation are executed.
Feel free to customize the logging and additional action logic to fit your specific requirements. Method swizzling offers great flexibility in enhancing existing methods while keeping the codebase clean and maintainable.
Upvotes: -1
Reputation: 17923
Inspired by Rengers's answer and apokryfos's comment:
tsconfig.json
:
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
example.ts
:
#!/usr/bin/env ts-node
export function SomeDecorator(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
console.log('SomeDecorator called');
}
export abstract class ExampleComponent {
abstract _canDoTheThing(): boolean;
@SomeDecorator
canDoTheThing(): boolean {
return this._canDoTheThing();
}
}
class ExampleComponentImpl extends ExampleComponent {
_canDoTheThing(): boolean {
return true;
}
}
new ExampleComponentImpl().canDoTheThing();
chmod u+x ./example.ts && ./example.ts
Output:
SomeDecorator called
Upvotes: 1
Reputation: 15218
I guess it depends on the context, but perhaps using a method that proxies can work for you?
export abstract class CanDeactivateComponent {
abstract canLeavePage(): boolean;
abstract onPageLeave(): void;
abstract canDeactivateBeforeUnload(): boolean;
@someDecorator
_canDeactivateBeforeUnload(): boolean {
return this.canDeactivateBeforeUnload()
}
}
Upvotes: 6