Here are the codes:
function a(fn: (a: string) => void) {
fn("Hello, World");
}
a(function b() {
return 1
});
By adding the type annotation to the callback of function a
, it means function a
must receive a function which has a argument of type string
and a return value of type void
. But when I passed function b
which couldn't satisfy the conditions of the type annotation, TS still didn't throw an compile error. Why?
Also, when I changed the value passed to fn
from "Hello, world"
(string
) to 123
(number
), it did throw an error. In this case, the type annotation worked.
CodePudding user response:
Consider this simple example:
type StringToVoid = (a: string) => void
type VoidToNumber = () => number
declare let a: StringToVoid
declare let b: VoidToNumber
a = b // ok
b = a // error
It is almost safe to assign b
to a
, just like in your example. Treat b
as a subtype of a
. Nothing bad happens if :
a=b;
// a is b
const result = a('str') // void, but in fact number
If you provide an argument to a function which is not expect any arguments, it does not affect your logic.
Same for return type. Function a
does not return anything. Hence you can't use it. If you assign b
to a
it return a value but TS will not allow you to do smth with it.
Whereas it is not safe to assign a
to b
since a
expect an argument which may lead to runtime error
b = a
b().toFixed(10) // runtime error
CodePudding user response:
Two things here:
Why are functions with fewer parameters assignable to functions that take more parameters?
and
Why are functions returning non-void assignable to function returning void?
The two links above are from the Typescript FAQ, and it should give you clarity about the said behaviour. Bottomline, this is expected outcome of the following two logical conclusions:
- The function that you are passing as the callback should be allowed to NOT use the parameter
a
of string type. - Returning more information when expected is none, that is
void
, is fine.
So, this won't work, because function b
expects a number while a
would pass a string.
// Argument of type '(p: number) => number' is not assignable to parameter of type '(a: string) => void'.
// Types of parameters 'p' and 'a' are incompatible.
// Type 'string' is not assignable to type 'number
a(function b(p: number) {
return 1
});
But this will work because b
has no use of string that function a
will pass to it... and it should be fine.
a(function b() {
return 1
});
Here is the link to TS Playground: https://tsplay.dev/WyObJN
function a(fn: (a: string) => void) {
fn("Hello, World");
}
a(function b() {
return 1
});
// Argument of type '(p: number) => number' is not assignable to parameter of type '(a: string) => void'.
// Types of parameters 'p' and 'a' are incompatible.
// Type 'string' is not assignable to type 'number
a(function b(p: number) {
return 1
});
function a1(fn: (a: string) => number) {
fn("Hello, World");
}
// Argument of type '(p: string) => string' is not assignable to parameter of type '(a: string) => number'.
// Type 'string' is not assignable to type 'number'.(2345)
a1(function b(p: string) {
return p;
});
So, shape mismatch in callback function will only work if the callback passed is compatible with the callback parameter.