Cornwell
Cornwell

Reputation: 3410

How to define multiple types depending on the property name of an object

I'm creating a browser extension and in order to communicate between background and frontend I need to use chrome.runtime.sendMessage()

In my frontend script, I want to send a message to the background, so I do this:

chrome.runtime.sendMessage({ action: 'setUpdate', value: { total: 4, alarm: false }  })

And in the background I receive it like this:

chrome.runtime.onMessage.addListener((message: BackgroundMessage) => {
  switch (message.action) {
     case 'setUpdate':
        ...
     break;
  }
});

My problem is creating the BackgroundMessage type, as each action will have a different value type.

I've tried this:

interface actions {
  setUpdate: { total: number; alarm: boolean };
  setReminder: { text: string; when: Date };
}

type BackgroundMessage = Record<keyof actions, actions>;

But I don't know how to specify that the keys of the action interface need to be in the action property, and the value types in the value property.

Is this possible?

Upvotes: 0

Views: 36

Answers (1)

tenshi
tenshi

Reputation: 26307

You already have defined a map of actions to values, so you could use a mapped type to make a discriminated union from that map:

interface Actions {
  setUpdate: { total: number; alarm: boolean };
  setReminder: { text: string; when: Date };
}

type BackgroundMessage = {
    [K in keyof Actions]: { action: K; value: Actions[K] };
}[keyof Actions];

Then you can narrow the type based on the action:

if (msg.action === "setReminder") {
    msg.value.text // ok
}

Playground

Upvotes: 1

Related Questions