I am trying to define a function that can wrap an arbitrary function while preserving parameter types and the return type. I have it working when the function doesn't have generics, but am struggling to get it working with generics. Here's a simplified example:
function wrap<F extends (...args: any[]) => any>(test: F) {
return (...args: Parameters<typeof test>): ReturnType<typeof test> => {
return test(...args);
};
}
function simpleTest(a: number): number {
return a;
}
// works
// type: (a: number) => number
const wrappedSimpleTest = wrap(simpleTest);
function genericTest<T>(a: T): T {
return a;
}
// doesn't work
// type: (a: unknown) => unknown
// desired type: <T>(a: T) => T
const wrappedGenericTest = wrap(genericTest);
function genericTest2<T, U>(a: T, b: U): T|U {
return Math.random() < 0.5 ? a : b;
}
// doesn't work
// type: (a: unknown, b: unknown) => unknown
// desired type: <T, U>(a: T, b: U) => T|U
const wrappedGenericTest2 = wrap(genericTest2);
CodePudding user response:
The specific behavior you're looking for here can be achieved by using TypeScript's support for higher order type inference from generic functions, implemented in microsoft/TypeScript#30215. Generally speaking the language is not able to abstract over generics this way in the type system; that would require higher kinded types as requested in microsoft/TypeScript#1213, and TypeScript doesn't have that. Instead, it uses some heuristics with some specific limitations and requirements to try to infer generic function types this way.
Here's one way to get it working:
function wrap<A extends any[], R>(test: (...args: A) => R) {
return (...args: A): R => {
return test(...args);
};
}
const wrappedGenericTest = wrap(genericTest);
// const wrappedGenericTest: <T>(a: T) => T