Home > Back-end >  Why it won't throw an error when the shape of parameter is different from that of argument in t
Why it won't throw an error when the shape of parameter is different from that of argument in t

Time:11-17

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:

  1. The function that you are passing as the callback should be allowed to NOT use the parameter a of string type.
  2. 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.

  • Related