I am attempting to wrap typescript functions, returning new functions that add extra behaviour. For example, in the minimal repro below, (see playground) the function after wrapping always returns a Promise and its arguments and return value are console-logged.
However, I have found that if the original function has any Generic typing, I don't know how to bind that Generic type so it is available on the wrapped function even if it is well-known in the calling scope.
The problem is demonstrated by const delayedValue
in the below script. Typescript seems to believe this can only be unknown.
Is there any way for e.g. wrappedDelay
to be defined such that the generic V
parameter can make it through and hence inform what return type we can expect from the wrappedDelay
function.
export type AnyFn = (...args: any[]) => any;
/** If sync function - ReturnType. If async function - unwrap Promise of ReturnType */
export type Result<Fn extends AnyFn> = Fn extends () => Promise<infer Promised>
? Promised
: ReturnType<Fn>;
/** Async function with same params and return as Fn, regardless if Fn is sync or async */
export type WrappedFn<Fn extends AnyFn> = (...parameters:Parameters<Fn>) => Promise<Result<Fn>>
/** Construct wrapped function from function reference */
function wrapFn<Fn extends AnyFn>(fn:Fn) : WrappedFn<Fn>{
return async (...parameters:Parameters<Fn>) => {
console.log(`Parameters are ${parameters}`);
const result = await fn(...parameters);
console.log(`Result is ${result}`);
return result;
}
}
function sum(a:number, b:number){
return a + b;
}
function delay<V>(value: V, ms:number){
return new Promise<V>((resolve, reject) => {
setTimeout(() => resolve(value), ms)
})
}
const wrappedSum = wrapFn(sum)
const wrappedDelay = wrapFn(delay)
async function example() {
const sum = await wrappedSum(3,4)
const delayedValue = await wrappedDelay("hello", 1000)
}