在 TypeScript 中创建电子菜单?

Posted

技术标签:

【中文标题】在 TypeScript 中创建电子菜单?【英文标题】:Create Electron Menu in TypeScript? 【发布时间】:2018-01-30 09:13:03 【问题描述】:

刚刚使用 TypeScript 启动了一个简单的 Electron 应用程序,我正在尝试设置我的自定义菜单。我按照JS中的例子,但是行

menu = Menu.buildFromTemplate(template); 编译失败,报错:

main.ts(109,35): error TS2345: Argument of type '( label: string; submenu: ( role: string; | type: string; )[]; | role: string; submenu...' is not assignable to parameter of type 'MenuItemConstructorOptions[]'.

我一定错过了什么。在任何地方都找不到“MenuItemConstructorOptions”类型(但我可能看错了地方)。我的 ma​​in.ts 的完整代码:

import  app, BrowserWindow, screen, Menu  from 'electron';
import * as path from 'path';

let win, menu;

function createWindow() 
    const electronScreen = screen;
    const size = electronScreen.getPrimaryDisplay().workAreaSize;

    win = new BrowserWindow(
        x: 0,
        y: 0,
        width: size.width,
        height: size.height
    );

    // and load the index.html of the app.
    win.loadURL('file://' + __dirname + '/index.html');
    win.webContents.openDevTools();
    win.on('closed', () => 
        win = null;
    );


function createMenu() 
    const template = [
            label: 'Edit',
            submenu: [
                 role: 'undo' ,
                 role: 'redo' ,
                 type: 'separator' ,
                 role: 'cut' ,
                 role: 'copy' ,
                 role: 'paste' ,
                 role: 'pasteandmatchstyle' ,
                 role: 'delete' ,
                 role: 'selectall' 
            ]
        ,
        
            label: 'View',
            submenu: [
                 role: 'reload' ,
                 role: 'forcereload' ,
                 role: 'toggledevtools' ,
                 type: 'separator' ,
                 role: 'resetzoom' ,
                 role: 'zoomin' ,
                 role: 'zoomout' ,
                 type: 'separator' ,
                 role: 'togglefullscreen' 
            ]
        ,
         role: 'window', submenu: [ role: 'minimize' ,  role: 'close' ] ,
        
            role: 'help',
            submenu: [
                label: 'Learn More',
                click()                            require('electron').shell.openExternal('https://electron.atom.io');
                
            ]
        
    ];
    menu = Menu.buildFromTemplate(template);
    Menu.setApplicationMenu(menu);


try 
    app.on('ready', function() 
        createWindow();
        createMenu();
    );
    app.on('window-all-closed', () => 
        if (process.platform !== 'darwin') 
            app.quit();
        
    );

    app.on('activate', () => 
        if (win === null) 
            createWindow();
        
    );
 catch (e) 
    throw e;

【问题讨论】:

【参考方案1】:

对我来说,将模板 const 的类型设置为 Electron.MenuItemConstructorOptions[] 就足够了。

例如:

const template: Electron.MenuItemConstructorOptions[] = [
        label: 'Edit',
        submenu: [
             role: 'undo' ,
             role: 'redo' ,
             type: 'separator' ,
             role: 'cut' ,
             role: 'copy' ,
             role: 'paste' ,
             role: 'pasteandmatchstyle' ,
             role: 'delete' ,
             role: 'selectall' 
        ]
    ,
    
        label: 'View',
        submenu: [
             role: 'reload' ,
             role: 'forcereload' ,
             role: 'toggledevtools' ,
             type: 'separator' ,
             role: 'resetzoom' ,
             role: 'zoomin' ,
             role: 'zoomout' ,
             type: 'separator' ,
             role: 'togglefullscreen' 
        ]
    ,
     role: 'window', submenu: [ role: 'minimize' ,  role: 'close' ] ,
    
        role: 'help',
        submenu: [
            label: 'Learn More',
            click() 
                require('electron').shell.openExternal('https://electron.atom.io');
            
        ]
    
];

【讨论】:

【参考方案2】:

对我来说,问题在于正确的角色大写:

   role: 'forceReload' ,
   role: 'toggleDevTools' 

结束

   role: 'forcereload' ,
   role: 'toggledevtools' 

见docs。

【讨论】:

【参考方案3】:

我无法在 TS 中获得适用于 JS 的示例。 MenuItemConstructorOptions 是电子包中electron.d.ts 文件中定义的接口。但是,我通过单独定义菜单条目并将它们推送到一个空数组找到了一种解决方法。有趣的是,里面的子菜单条目被接受并且没有问题地工作。这是有效的代码:

  function createMenu() 
     const template = [];
     // Edit Menu
     template.push(
        label: 'Edit',
        submenu: [
            role: 'undo' ,
            role: 'redo' ,
            type: 'separator' ,
            role: 'cut' ,
            role: 'copy' ,
            role: 'paste' ,
            role: 'pasteandmatchstyle' ,
            role: 'delete' ,
            role: 'selectall' 
        ]
     );
     // View Menu
     template.push(
        label: 'View',
        submenu: [
            role: 'reload' ,
            role: 'forcereload' ,
            role: 'toggledevtools' ,
            type: 'separator' ,
            role: 'resetzoom' ,
            role: 'zoomin' ,
            role: 'zoomout' ,
            type: 'separator' ,
            role: 'togglefullscreen' 
        ]
     );
     // Windown menu
     template.push(
        role: 'window',
        submenu: [ role: 'minimize' ,  role: 'close' ]
     );
     // Help menu
     template.push(
        role: 'help',
        submenu: [
           
              label: 'Learn More',
              click() 
                 require('electron').shell.openExternal('https://electron.atom.io');
              
           
        ]
     );

     if (process.platform === 'darwin') 
        template.unshift(
           label: app.getName(),
           submenu: [
               role: 'about' ,
               type: 'separator' ,
               role: 'services', submenu: [] ,
               type: 'separator' ,
               role: 'hide' ,
               role: 'hideothers' ,
               role: 'unhide' ,
               type: 'separator' ,
               role: 'quit' 
           ]
        );

        // Edit menu
        template[1].submenu.push(
            type: 'separator' ,
            label: 'Speech', submenu: [ role: 'startspeaking' ,  role: 'stopspeaking' ] 
        );

        // Window menu
        template[3].submenu = [ role: 'close' ,  role: 'minimize' ,  role: 'zoom' ,  type: 'separator' ,  role: 'front' ];
     

     menu = Menu.buildFromTemplate(template);
     Menu.setApplicationMenu(menu);
  

如果有人遇到同样的问题,希望这会有所帮助

【讨论】:

【参考方案4】:

在 MenuItemConstructorOptions 接口中,子菜单属性定义为联合类型。因此需要将该属性强制转换为 MenuItemConstructorOptions 数组,以便识别推送操作符:

(windowMenu.submenu as MenuItemConstructorOptions[]).push(
  
    type: 'separator',
  ,
  
    label: 'Bring All To Front',
    role: 'front'
  
);

【讨论】:

【参考方案5】:

当我将菜单语法基于 Electron 的 Menu example 时,我遇到了同样的错误,它在 javascript 中运行良好,但会扰乱 TypeScript。不幸的是,将template 转换为suggested by PhoneixS 的MenuItemConstructorOptions[] 对我不起作用。

在 Electron 的示例中,TypeScript 似乎在三元组上跳闸,并且无法确定它们的结果类型。在我的例子中,在包含三元组的右括号之后添加 as MenuItemConstructorOptions[] 使 TypeScript 意识到它们毕竟是有效的子菜单。

有效 TypeScript 中的完整 Electron 示例:

import  app, Menu, MenuItemConstructorOptions, shell  from "electron"

const isMac = process.platform === 'darwin'

const menu = Menu.buildFromTemplate(
  [
    //  role: 'appMenu' 
    ...(isMac ? [
      label: app.name,
      submenu: [
         role: 'about' ,
         type: 'separator' ,
         role: 'services' ,
         type: 'separator' ,
         role: 'hide' ,
         role: 'hideothers' ,
         role: 'unhide' ,
         type: 'separator' ,
         role: 'quit' 
      ]
    ] : []) as MenuItemConstructorOptions[],
    //  role: 'fileMenu' 
    
      label: 'File',
      submenu: [
        isMac ?  role: 'close'  :  role: 'quit' 
      ] as MenuItemConstructorOptions[]
    ,
    //  role: 'editMenu' 
    
      label: 'Edit',
      submenu: [
         role: 'undo' ,
         role: 'redo' ,
         type: 'separator' ,
         role: 'cut' ,
         role: 'copy' ,
         role: 'paste' ,
        ...(isMac ? [
           role: 'pasteAndMatchStyle' ,
           role: 'delete' ,
           role: 'selectAll' ,
           type: 'separator' ,
          
            label: 'Speech',
            submenu: [
               role: 'startSpeaking' ,
               role: 'stopSpeaking' 
            ]
          
        ] : [
           role: 'delete' ,
           type: 'separator' ,
           role: 'selectAll' 
        ]) as MenuItemConstructorOptions[]
      ]
    ,
    //  role: 'viewMenu' 
    
      label: 'View',
      submenu: [
         role: 'reload' ,
         role: 'forceReload' ,
         role: 'toggleDevTools' ,
         type: 'separator' ,
         role: 'resetZoom' ,
         role: 'zoomIn' ,
         role: 'zoomOut' ,
         type: 'separator' ,
         role: 'togglefullscreen' 
      ]
    ,
    //  role: 'windowMenu' 
    
      label: 'Window',
      submenu: [
         role: 'minimize' ,
         role: 'zoom' ,
        ...(isMac ? [
           type: 'separator' ,
           role: 'front' ,
           type: 'separator' ,
           role: 'window' 
        ] : [
           role: 'close' 
        ]) as MenuItemConstructorOptions[]
      ]
    ,
    
      role: 'help',
      submenu: [
        
          label: 'Learn More',
          click: async () => 
            await shell.openExternal('https://electronjs.org')
          
        
      ]
    
  ]
)

Menu.setApplicationMenu(menu)

【讨论】:

【参考方案6】:

我猜你有一个错误/错字:

 type: 'separator' ,

TypeScript 无法解析类型,因为未知的 type 属性(根据您的其他输入应该是 role

【讨论】:

官方文档:github.com/electron/electron/blob/master/docs/api/menu-item.md 说“type”是一个可选属性,允许值为 normal、separator、submenu、checkbox 或 radio @stwissel 这可能是真的。但是打字稿不明白一些东西。并且应该在这里检查MenuItemConstructorOptions 类型定义,而不是文档。 TypeScript 无法将您的数组数据类型与定义匹配。请问MenuItemConstructorOptions的定义能发一下吗? 这很有趣:我在任何地方都找不到MenuItemConstructorOptions 的定义。似乎从打字中丢失了。猜猜我必须创建它 我很确定它存在。很可能它是在每次构建期间从头开始创建的(如果您查看 package.json 脚本,看起来就是这样)。无论如何,TypeScript 都知道它,否则它不会给出错误is not assignable to parameter of type 'MenuItemConstructorOptions[]'

以上是关于在 TypeScript 中创建电子菜单?的主要内容,如果未能解决你的问题,请参考以下文章

如何像在 babelrc 中创建依赖别名一样在 typescript 中创建类型别名?

TypeScript 中的“类型”保留字是啥?

如何在 TypeScript 中创建循环引用类型?

如何在 TypeScript 中创建本地模块

使用 Typescript 在 Sequelize 模型中创建实例方法

使用 RxJS 在 TypeScript 中创建 BaseObserver