TransWikia.com

How can I make Electron channels more type safe?

Stack Overflow Asked by user1283776 on December 22, 2021

Here is how I handle communication over Electron channels:

preload.ts

contextBridge.exposeInMainWorld("myIpcRenderer", {
  invoke: (channel: Channel, ...args: any[]) =>
    callIpcRenderer("invoke", channel, ...args),
  send: (channel: Channel, ...args: any[]) =>
    callIpcRenderer("send", channel, ...args),
  on: (channel: Channel, ...args: any[]) =>
    callIpcRenderer("on", channel, ...args),
});

types.d.ts

interface MyIpcRenderer {
  invoke(channel: Channel.ReadFiles, ...args: any[]): Promise<ReadFileResult[]>;
}

The MyIpcRenderer type enforce correct use in renderer.ts:

const files = await window.myIpcRenderer.invoke(Channel.ReadFiles, [
  svgPath,
]);

But it does not enforce correct use in main.ts. ipcMain.handle refers to an electron method that can accept channels with any name and retturn any kind of promise.

ipcMain.handle(
  "anychannelnamegoes",
  async (_event, paths: string[]): Promise<any> => {
    ...
  }
);

How can I rewrite my code to enforce main usage in main.ts as well?

One Answer

I see two ways to do this.

First possibility is to override Electron library types declaration in your typings.d.ts file:

declare module 'electron' {
  export interface IpcMain extends NodeJS.EventEmitter {
    handle(
      channel: Channel,
      listener: (
        event: IpcMainInvokeEvent,
        ...args: any[]
      ) => Promise<void> | any
    ): void;
  }
}

Considering that Channel is a string literal (type Channel = 'channel1' | 'channel2'), you would get suggestions for 'channel1' and 'channel2', but the initial signature channel: string would still be allowed and any string would be accepted.

The second possibility I see is to wrap it, which would give better type safety:

const myHandler = (
  channel: Channel,
  listener: (
    event: IpcMainInvokeEvent,
    ...args: any[]
  ) => Promise<void> | any
) => ipcMain.handle(channel, listener);

// only 'channel1' and 'channel2' would be accepted
myHandler('channel1', (event) => {});

I you would like to type the arguments currently declared with the spread operator ...args: any[] you would need to declare the parameters explicitly in both cases:

...
listener: (
  event: IpcMainInvokeEvent,
  arg1: string[],
  arg2: boolean,
  arg3: number
) => Promise<void> | any
...

Answered by Guillaume on December 22, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP