I have a electron project which has strucutre:
src - main - preload - preload.ts - preload.d.ts - render - shared
I want to inject some native code into my renderer code, so I use proload like this:
// preload.ts
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron'
import { IpcChannels } from '@shared/channelNames'
contextBridge.exposeInMainWorld('api', {
ipcSend(channel: IpcChannels, ...data: any[]) {
ipcRenderer.send(channel, ...data)
},
ipcSendSync(channel: IpcChannels, ...data: any[]) {
return ipcRenderer.sendSync(channel, ...data)
},
ipcOn(channel: IpcChannels, listener: (event: IpcRendererEvent, ...args: any[]) => void) {
ipcRenderer.on(channel, listener)
},
ipcOnce(channel: IpcChannels, listener: (event: IpcRendererEvent, ...args: any[]) => void) {
ipcRenderer.once(channel, listener)
},
ipcOff(channel: IpcChannels) {
ipcRenderer.removeAllListeners(channel)
}
})
// channelNames.ts
export const WINDOW_MINIMIZE = 'WINDOW_MINIMIZE'
export const WINDOW_MAXIMIZE = 'WINDOW_MAXIMIZE'
export const WINDOW_UNMAXIMIZE = 'WINDOW_UNMAXIMIZE'
export const WINDOW_CLOSE = 'WINDOW_CLOSE'
export const IS_WINDOW_MAXIMIZE = 'IS_WINDOW_MAXIMIZE'
export type IpcChannels =
| 'WINDOW_MINIMIZE'
| 'WINDOW_MAXIMIZE'
| 'WINDOW_UNMAXIMIZE'
| 'WINDOW_CLOSE'
| 'IS_WINDOW_MAXIMIZE'
I add preload.d.ts
into include
array in my renderer's tsconfig.json
:
{
"compilerOptions": {
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"@@/*": ["../../*"],
"@/*": ["*"],
"@shared/*": ["../shared/*"]
}
},
"include": [
"./**/*.ts",
"./**/*.tsx",
"./**/*.less",
"../shared/**/*.ts",
"../../types/**/*.d.ts",
"../preload/preload.d.ts"
]
}
Then I write preload.d.ts
like this:
import { IpcChannels } from '@shared/channelNames'
type IpcListener = (event: Electron.IpcRendererEvent, ...args: any[]) => void
interface Api {
readonly version: string
readonly ipcSend(channel: IpcChannels, ...data: any[]): void
readonly ipcSendSync(channel: IpcChannels, ...data: any[]): any
readonly ipcOn(channel: IpcChannels, listener: IpcListener): void
readonly ipcOnce(channel: IpcChannels, listener: IpcListener): void
readonly ipcOff(channel: IpcChannels): void
}
interface Window {
readonly api: Api
}
But I can't get correct type hints, window.api
is any
type.
If I remove first line import and copy IpcChannels
, it works:
type IpcChannels =
| 'WINDOW_MINIMIZE'
| 'WINDOW_MAXIMIZE'
| 'WINDOW_UNMAXIMIZE'
| 'WINDOW_CLOSE'
| 'IS_WINDOW_MAXIMIZE'
type IpcListener = (event: Electron.IpcRendererEvent, ...args: any[]) => void
interface Api {
readonly version: string
readonly ipcSend(channel: IpcChannels, ...data: any[]): void
readonly ipcSendSync(channel: IpcChannels, ...data: any[]): any
readonly ipcOn(channel: IpcChannels, listener: IpcListener): void
readonly ipcOnce(channel: IpcChannels, listener: IpcListener): void
readonly ipcOff(channel: IpcChannels): void
}
Is there any way I can import IpcChannels
rather than copy it? Otherwise I have to write twise if I want to add a channel name.
https://stackoverflow.com/a/51114250/12568197
.d.ts
files are treated as an ambient module declarations only if they don't have any imports. If you provide an import line, it's now treated as a normal module file, not the global one, so augmenting modules definitions doesn't work.
For my case it should be:
type IpcChannels = import('@shared/channelNames').IpcChannels
type IpcListener = (event: Electron.IpcRendererEvent, ...args: any[]) => void
interface Api {
readonly version: string
readonly ipcSend(channel: IpcChannels, ...data: any[]): void
readonly ipcSendSync(channel: IpcChannels, ...data: any[]): any
readonly ipcOn(channel: IpcChannels, listener: IpcListener): void
readonly ipcOnce(channel: IpcChannels, listener: IpcListener): void
readonly ipcOff(channel: IpcChannels): void
}
interface Window {
readonly api: Api
}