【问题标题】:Create Electron Menu in TypeScript?在 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;
}

【问题讨论】:

    标签: typescript electron typescript-typings tsc


    【解决方案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 PhoneixSMenuItemConstructorOptions[] 对我不起作用。

            在 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[]'
              猜你喜欢
              • 1970-01-01
              • 2017-12-24
              • 1970-01-01
              • 2017-09-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-08-19
              • 2013-12-24
              相关资源
              最近更新 更多