我有一个 electron 项目,它有如下的目录结构:
src
- main // 主进程
- preload // preload
- preload.ts
- preload.d.ts
- render // 渲染进程
- shared // 共用
我想向渲染进程注入一些底层代码,于是我用了 preload:
// 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'
我把 preload.d.ts
加到了渲染进程的 tsconfig.json
中的 include
数组里:
{
"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"
]
}
然后我是这样写 preload.d.ts
的:
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
}
不过这样行不通,我键入 window.api
的时候没有获得任何提示,鼠标放到 api
上是显示的类型是 any
。
但如果我不使用 import
语句,而是把 IpcChannels
复制过来:
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
}
这样是可以的,window.api
的类型显示为 Api
。但这样写我每次新加一个频道名就要改两个地方,有可能会忘。所有有什么好办法可以在 .d.ts
声明文件中引入其他 .ts
类型文件定义的类型吗?
stackoverFlow上找到了同样的问题,答案在这里:
如何在.d.ts中引入类型
省流:
错误原因:
解决方案:TS2.9开始支持使用import()语法引入类型。