I'm struggling with creating a function that can add functionality to another function in a generic way. Here's my current approach:
/**
* Creates a function that first calls originalFunction, followed by newFunction.
* The created function returns the value returned by the original function
* returned by the original function
* @param originalFunction Function to be invoked first
* @param newFunction Function to be invoked second
*/
function callAfter<T extends Function>(originalFunction: T, newFunction: T): any {
return function() {
const result = originalFunction.apply(this, arguments);
newFunction.apply(this, arguments);
return result;
};
}
However, I'm encountering an error when trying to compile the above code:
TS2322: Type '() => any' is not assignable to type 'T'
I'm wondering if there's a way to maintain type safety while achieving this functionality? Right now, I've settled for making it return any
to get it "working."
In continuing my exploration, I came up with the following modification:
/**
* Creates a function that first calls originalFunction, followed by newFunction. The resulting function
* returns the value from the original function
* @param originalFunction Function to be called first
* @param newFunction Funct
*/
function callAfter<R, T extends () => R>(originalFunction: T, newFunction: T): T {
return <T> function () {
const result: R = originalFunction.apply(this, arguments);
newFunction.apply(this, arguments);
return result;
};
}
Unfortunately, this version doesn't work for functions that return void, which was my primary use case at the moment.
The goal of my code snippet is to utilize a generic higher order function to enhance functionality in TypeScript.
/**
* Component can leverage this feature to automatically remove subscriptions
* when the component is no longer rendered in the DOM
*/
class SubscriptionTracker {
private subscriptions: Subscription[] = [];
constructor(destroyable: OnDestroy) {
destroyable.ngOnDestroy = callAfter(destroyable.ngOnDestroy, () => {
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
});
}
subscribe<T>(observable: Observable<T>, observer: PartialObserver<T>): Subscription {
const subscription = observable.subscribe(observer);
this.subscriptions.push(subscription);
return subscription;
}
unsubscribe(subscription: Subscription) {
subscription.unsubscribe();
const indexOfSubscription = this.subscriptions.indexOf(subscription);
if (indexOfSubscription == -1) {
throw new Error('Unsubscribing to untracked subscription');
}
this.subscriptions.splice(indexOfSubscription, 1);
return subscription;
}
}