Reputation: 178
I've got some pretty simple function that receives a function and parameters and executes the function with that parameters. I've written code like this:
type Action<Payload> = (payload: Payload) => any;
type SomeType = string;
declare function execute<Payload>(action: Action<Payload>, payload: Payload);
declare function testFn<P extends SomeType>(payload: P): number;
execute(testFn, '');
run in typescriptlang.org repl
But it produces the error:
Argument of type '<P extends string>(payload: P) => number' is not assignable to parameter of type 'Action<{}>'.
Types of parameters 'payload' and 'payload' are incompatible.
Type '{}' is not assignable to type 'string'.
The fact is that if I change the order of the argument it makes typescript to infer the types correctly:
type Action<Payload> = (payload: Payload) => any;
type SomeType = string;
declare function execute<Payload>(payload: Payload, action: Action<Payload>);
declare function testFn<P extends SomeType>(payload: P): number;
execute('hell yeah!', testFn);
run in typescriptlang.org repl
Is there a way to make it work without changing the order? And why does it seem like typescript always tries inferring types left to right?
UPD:
It seems to be missing part in TypeScript itself:
Upvotes: 3
Views: 1119
Reputation: 329718
This is an interesting problem. After playing around with it a bit I think I found a signature that has the proper behavior:
declare function execute<P>(action: Action<any> & Action<P>, payload: P): void;
The intersection type seems to delay the evaluation of Action<P>
until after P
has been inferred:
execute(testFn, ''); // okay
execute(testFn, 123); // error, 123 is not a string
I don't really know why this happens (perhaps someone more familiar with the internals of the compiler can say something better here) but maybe it's enough to help you make progress? Good luck!
Upvotes: 1