I'm trying to create a higher order function that can attach a primitive watcher to a method. It's working great as long as the input function is not overloaded, but I'm running into issues with overloads. I would like to find a way to pass all overloads through the higher order function so the returned function can behave just like the original. This is what I have so far:
I build a watcher with a function like this, giving me a simple way to track statistics of function use.
function makeWatcher() {
let _callCount = 0;
function countCall() {
_callCount = _callCount + 1;
}
function getCallCount() {
return _callCount;
}
return {
countCall,
getCallCount,
};
}
I then have a higher order function that takes a function and a watcher as input, and returns a function that interacts with the watcher and then runs the original function.
// Generic type assures method signature is inferred.
function watchMethod<T extends (...args: any[]) => any>(
func: T,
watcher: Watcher
): (...funcArgs: Parameters<T>) => ReturnType<T> {
// Return a new function that has same signature as input function,
// with added watcher interaction when called
return (...args: Parameters<T>): ReturnType<T> => {
// Interact with watcher
watcher.countCall();
// execute original function and return results
const results = func(...args);
return results;
};
}
The last step is to compose these together to give me a method that has properties I can interact with, like so:
function buildWatchedMethod<T extends (...args: any[]) => any>(inputFunction: T) {
// get a watcher
const watcher = makeWatcher();
// set up method watching
const watchedMethod= watchMethod(inputFunction, watcher);
// list out properties that should be public
const externalProperties = {
getCallCount: watcher.getCallCount
}
// assign properties to method
return Object.assign(watchedMethod, externalProperties);
}
I can then add watchers to methods like this:
function method(input: string){
console.log(input);
}
const watchedMethod = buildWatchedMethod(method);
watchedMethod("Hello World"); // logs "Hello World"
watchedMethod.getCallCount(); // returns 1 - method was called once
watchedMethod("Hello again"); // logs "Hello Again"
watchedMethod.getCallCount(); // returns 2
watchedMethod.countCall(); // error - not available from outside function
This works beautifully, function signatures are preserved, type safety is preserved - until buildWatchedMethod is called with an overloaded method. It still works, but the overloads of the watched methods aren't preserved through the process, which makes typescript complain.
This appears to be expected behavior as outlined in the accepted answer of this post, but is there any way to carry the overload signatures through the higher order function?
Where the issue comes up most often is when I use libraries where overloads are common. (I'm looking at you, Mongoose!) I'm also trying to avoid mocking libraries like Sinon because a) they can be painful to use with typescript sometimes, and b) my needs for this use case are very minimal, and third party libraries tend to have a lot more involved than what I need.
Any suggestions are appreciated!