TypeScript Version: 3.4.0-dev.20190202
Search Terms:
2322Parameters ReturnTypeCode
function wrap<Fn extends (...args: any) => any>(fn: Fn): Fn {
type P = Parameters<Fn>;
type R = ReturnType<Fn>;
return function(...args: P): R {
return fn(...args);
};
}
Expected behavior:
I would expect this to pass the type checker. I'm returning a function with the same parameter and return type as Fn, so I'd expect it to be assignable to Fn.
Actual behavior:
I get this error:
[ts] Type '(...args: Parameters<Fn>) => ReturnType<Fn>' is not assignable to type 'Fn'. [2322]
utils.ts(369, 10): Did you mean to call this expression?
Playground Link:
Related Issues:
The full context is that I want to write a wrapper which memoizes just the last invocation of a function. I wound up with:
export function memoizeOne<Fn extends (...args: any) => any>(fn: Fn): Fn {
type P = Parameters<Fn>;
type R = ReturnType<Fn>;
let lastArgs: P = null;
let lastResult: R;
return function(...args: P) {
if (shallowEqual(args, lastArgs)) {
return lastResult;
}
lastArgs = args;
lastResult = fn(...args);
return lastResult;
} as any; // XXX
}
but I don't think that as any should be needed.
The contract associated with a function of type <Fn extends (...args: any) => any>(fn: Fn): Fn is _very_ strong; the only real way to return a value of type Fn is to return the value you were given.
If a caller were to pass a function with properties then the type suggests they would get back a value with the same properties: the wrap function does not do this.
interface A {
prop: number;
(x: number): number;
}
declare const a: A;
const p: number = wrap(a).prop;
I think the best bet is to be directly parametric in the input and output types.
function wrap<P extends any[], R>(fn: (...args: P) => R): (...args: P) => R {
return function(...args: P): R {
return fn(...args);
};
}
Makes perfect sense, thanks for the explanation @jack-williams!
Most helpful comment
The contract associated with a function of type
<Fn extends (...args: any) => any>(fn: Fn): Fnis _very_ strong; the only real way to return a value of typeFnis to return the value you were given.If a caller were to pass a function with properties then the type suggests they would get back a value with the same properties: the wrap function does not do this.
I think the best bet is to be directly parametric in the input and output types.