Home > Software design >  How to define multiple types depending on the property name of an object
How to define multiple types depending on the property name of an object

Time:01-14

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?

CodePudding user response:

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

  • Related