Type checking when passing functions as parameters

Topics: General, Language Specification
Jul 31, 2013 at 3:28 PM
Given the following code:
declare function f1( p: (a: any, b: any) => any );
declare function f2( p: (a: any, b?: any) => any );

f1( (a: any): any => a );    // test 1 - ok
f1( (a: any, b: any): any => a );  // test 2 - ok 

f2( (a: any): any => a );    // test 3 - ok
f2( (a: any, b: any): any => a );  // test 4 - fail
f2( (a: any, b?: any): any => a ); // test 5 - ok
I expected that for f1 test 1 will fail (because the parameter passed has a different type than the type specified in the function declaration) and test 2 will pass. For f2 I expect that both test 3 and test 4 will pass because the second parameter is optional in the declaration.

However the compiler behaves differently as test 1 passes while test 4 fails. I don't understand why.
Jul 31, 2013 at 4:50 PM
The easiest way to reason about these things is to think of p as a callback function. f1 or f2 will invoke p with 2 or (1 or 2) parameters, respectively. You're allowed to use a function with fewer parameters (e.g. you can pass a one-argument function to Array.forEach), but you can't pass a function with more required parameters than the caller might provide.
Jul 31, 2013 at 9:25 PM
More details on the topic in this thread:

Aug 1, 2013 at 7:32 AM
Thanks, I think I understand now. f1 will always call p with 2 parameters but f2 will call p with either 1 or 2 parameters. So an implementation of f1 and f2 might be:
function f1( p: (a: any, b: any) => any )
  p(10, 20); // call p always with 2 parameters

function f2( p: (a: any, b?: any) => any )
  // call p with 1 or 2 parameters depending on some condition
  if (some_condition)
So f1 always calls p with 2 parameters and the caller of f1 is free the supply a callback that uses 2 or fewer parameters. f2 however actually burdens the caller to know which condition causes it to decide on the number of parameters when calling p; not a very useful use case.