I'm attempting to create a higher-order function that wraps inputted callbacks with a block of code and returns it. I want the returned function to have the same parameters, but a slightly different return value.
I've already tried something like this:
const wrapper = <Callback extends (...args: any) => any>(fn: Callback) => {
return (...newFnArgs: Parameters<typeof fn>) => {
try {
const val = fn(...newFnArgs as any /* lazy type cast */)
return {val} as {val: ReturnType<Callback>}
} catch {
return {val: null}
}
}
}
// works fine
const concreteFuntion = (n: number) => n
// inferred as (n: number) => {val: number} | {val: null}
const wrappedConcrete = wrapper(concreteFuntion)
// with generics doesn't work
const genericFunction: <T>(a: T) => T = (n) => n
// why is generic inferred as "unknown"
// inferred as (n: unknown) => {val: unknown} | {val: null}
const wrappedGeneric = wrapper(genericFunction)
but unfortunately whenever a function has a generic, the generic is inferred as unknown. Why is this happening? Is there any way to allow this to work with generic functions too?
CodePudding user response:
I honestly don't see the point all the gymnastics you're getting into with Parameters
, ReturnType
, explicit casts, etc. Why not just make the wrap()
function generic with a type parameter for the arguments and a type parameter for the return type?
const wrap = <A extends unknown[], R>(
fn: (...args: A) => R
): (...args: A) => { val: R | null} => {
return (...args: A) => {
try { return { val: fn(...args) }; }
catch { return { val: null }; }
}
};
const concreteFuntion = (n: number, s: string) => n s;
const wrappedConcrete = wrap(concreteFuntion);
// ↑ inferred as (n: number. s: string) => { val: string | null; }
const genericFunction: <T>(a: T) => T = (n) => n;
const wrappedGeneric = wrap(genericFunction);
// ↑ inferred as <T>(a: T) => { val: T | null; }