If the return value derives its rest parameter type from F
, how could F
be instantiated with a different subtype?
function wrap<F extends (...a: Parameters<F>) => number>(f: F): F {
return (...a: Parameters<F>) => f(...a)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
Type '(...a: Parameters<F>) => number' is not assignable to type 'F'.
'(...a: Parameters<F>) => number' is assignable to the constraint of type 'F', but 'F' could be instantiated with a different subtype of constraint '(...a: Parameters<F>) => number'.
>Solution :
Functions in JavaScript are objects and are therefore allowed to have properties like other objects. And just like T extends {a: string}
, is allowed to have more properties than just a
, F extends (...a: any) => any
is allowed to have more properties than just those found on Function.prototype
:
function magicFunction(x: string, y: number) {
return x.length + y;
}
magicFunction.magic = "abracadabra!!";
Or equivalently
const magicFunction = Object.assign(
(x: string, y: number) => x.length + y,
{ magic: "abracadabra!!" }
);
So the type of magicFunction
is
// const magicFunction: ((x: string, y: number) => number) & { magic: string; }
which has a magic
property of type string
.
The call signature of wrap()
claims to return the same exact type as its argument:
const wrappedMagicFunction = wrap(magicFunction);
// const wrappedMagicFunction: typeof magicFunction
But the implementation only returns a function of type ((x: string, y: number) => number)
:
console.log(wrappedMagicFunction("abcde", 8)) // 13, okay
And not necessarily a function of the same type as the input; in particular, it does not have the requisite magic
property:
console.log(wrappedMagicFunction.magic.toUpperCase()); // compiles okay, but
// 💥 RUNTIME ERROR! wrappedMagicFunction.magic is undefined
Since the implementation cannot be verified to satisfy the call signature, the compiler complains.