Home > OS >  How to do a type assertion for a callback argument?
How to do a type assertion for a callback argument?

Time:09-20

The company I work at has some code that looks something like this:

interface Command {
    action: string
}

interface Notification {
    event: string
}

type Message = Command | Notification;

function func(type: string, callback: (msg: Message) => void) {
    // ...
}

func("command", (c: Command) => console.log(c.action) )

The second argument of the function call on the last line is underlined with the following type error:

Argument of type '(c: Command) => void' is not assignable to parameter of type '(msg: Message) => void'.

Types of parameters 'c' and 'msg' are incompatible.

Type 'Message' is not assignable to type 'Command'.

Property 'action' is missing in type 'Notification' but required in type 'Command'.ts(2345)

(The third line of this error seems to be the reverse of the first line.) The thing is that in this case I know that the argument given to the callback will be a Command and not anything else. Is there a way to assert this to remove the error?

CodePudding user response:

Write the callback function so it will accept a Message, and then narrow it.

const callback = (c: Message) => {
    if ('action' in c) { // Commands must have an action and Notifications must not
        console.log(c.action)
    } else {
        // For example
        throw new Error("Expected a Command but received a Notification");
    }
};

CodePudding user response:

It displays the error since a Command or a Notification type can be passed into the function callback. But in line last line, when calling the function you restricted the callback argument to be only of the type Command.

Replacing the last line with this will solve the error, but be sure only an argument of type Command will be passed into the callback.

func("command", (c: Message) => console.log((c as Command).action) )

CodePudding user response:

The problem is simple,as @Augustine mentioned in his answer.

It displays the error since a Command or a Notification type can be passed into the function callback. But in line last line, when calling the function you restricted the callback argument to be only of the type Command.

Using the type assertion as mentioned by the @Augustine is not best way. Because, type assertions are only compile-time. Therefore, you have to make sure, that the value passed down is always of type Command which in real-time, may not possible.

Type assertions are like " hey compiler, I know this value is of type Command, so believe me and infer it as of type Command."

But in run-time, this can blow your code.

The ideal solution is Type Narrowing.

So, you use the broader type "Message" as expected by the compiler and then you narrow down the type to what you need and perform the task.

type Command = {
  action: string;
};

type Notification = {
  event: string;
};

type Message = Command | Notification;

function func(type: string, callback: (msg: Message) => void) {
  // ...
}

func("command", (c: Message) => {
  // Message can be of type command or notification but you narrow it down by using the  properties.
  // if "action" is there, it is of type "Command" and if "event" is there, its "Notification"
  if ("action" in c) {
    // perform the action accordingly
    console.log(c.action);
  } else if ("event" in c) {
    // perform the action accordingly for "Notification" case
  }
});
  • Related