如何在TypeScript中使用Ant Design构建动态菜单?
解决Ant Design TypeScript动态菜单类型错误问题
在使用TypeScript构建Ant Design动态菜单组件时,参考JavaScript方案后遇到类型报错:item仅被识别为拥有className, key, style属性的类型,导致icon={item?.icon}和{item?.label}处构建失败。
问题原因
Ant Design的MenuProps['items']是一个联合类型,包含了菜单项、子菜单、分割线等多种类型,TypeScript默认会推断为所有类型的交集——仅保留所有类型共有的className, key, style属性,因此无法识别icon、label这些专属菜单项的属性。
解决方案
以下是几种可行的修复方式:
方式一:显式指定菜单项类型
从antd导入MenuItemProps,将菜单数组的类型指定为MenuItemProps[],明确告诉TypeScript这是标准菜单项的数组:
import { useState, useEffect } from 'react'; import { Menu, type MenuProps, type MenuItemProps } from 'antd'; // 导入你的图标组件 import IconSetting from './icons/IconSetting'; import IconUpgrade from './icons/IconUpgrade'; import IconHelp from './icons/IconHelp'; import IconAbout from './icons/IconAbout'; import IconImport from './icons/IconImport'; import IconExport from './icons/IconExport'; function CustomAppBarMenu() { // 显式指定类型为MenuItemProps数组 const [menuItems, setMenuItems] = useState<MenuItemProps[]>([]); const main_window_menu_items: MenuItemProps[] = [ { key: 'setting', label: 'Setting', icon: <IconSetting />, }, { key: 'upgrade', label: 'Upgrade', icon: <IconUpgrade />, disabled: true, }, { key: 'help', label: 'Help', icon: <IconHelp />, }, { key: 'about', label: 'About', icon: <IconAbout />, }, ]; const edit_menu_items: MenuItemProps[] = [ { key: 'import', label: 'Import', icon: <IconImport />, }, { key: 'export', label: 'Export', icon: <IconExport />, }, ]; const handleMenuClick: MenuProps['onClick'] = ({ key }) => { console.log(`Click on item ${key}`); }; useEffect(() => { // 假设appWindow是已定义的变量 if (appWindow.label === 'main') { setMenuItems(main_window_menu_items); } else if (appWindow.label === 'edit') { setMenuItems(edit_menu_items); } // 添加依赖项避免无限循环 }, [appWindow.label]); return ( <Menu onClick={handleMenuClick}> {menuItems?.map((item) => ( <Menu.Item key={item.key} icon={item.icon}> {item.label} </Menu.Item> ))} </Menu> ); }
方式二:使用Menu组件的items属性(更简洁)
Ant Design的Menu组件支持直接通过items属性传入菜单配置,无需手动渲染Menu.Item,这种方式下TypeScript能自动识别类型:
import { useState, useEffect } from 'react'; import { Menu, type MenuProps } from 'antd'; // 导入你的图标组件 import IconSetting from './icons/IconSetting'; import IconUpgrade from './icons/IconUpgrade'; import IconHelp from './icons/IconHelp'; import IconAbout from './icons/IconAbout'; import IconImport from './icons/IconImport'; import IconExport from './icons/IconExport'; function CustomAppBarMenu() { const [menuItems, setMenuItems] = useState<MenuProps['items']>([]); const main_window_menu_items: MenuProps['items'] = [ { key: 'setting', label: 'Setting', icon: <IconSetting />, }, { key: 'upgrade', label: 'Upgrade', icon: <IconUpgrade />, disabled: true, }, { key: 'help', label: 'Help', icon: <IconHelp />, }, { key: 'about', label: 'About', icon: <IconAbout />, }, ]; const edit_menu_items: MenuProps['items'] = [ { key: 'import', label: 'Import', icon: <IconImport />, }, { key: 'export', label: 'Export', icon: <IconExport />, }, ]; const handleMenuClick: MenuProps['onClick'] = ({ key }) => { console.log(`Click on item ${key}`); }; useEffect(() => { if (appWindow.label === 'main') { setMenuItems(main_window_menu_items); } else if (appWindow.label === 'edit') { setMenuItems(edit_menu_items); } }, [appWindow.label]); // 直接通过items属性传入,无需手动映射Menu.Item return <Menu onClick={handleMenuClick} items={menuItems} />; }
方式三:类型断言(临时方案)
如果需要保留手动渲染Menu.Item的方式,也可以通过类型断言告诉TypeScript当前item是标准菜单项:
// 在map的时候添加类型断言 {menuItems?.map((item) => { const menuItem = item as MenuItemProps; return ( <Menu.Item key={menuItem.key} icon={menuItem.icon}> {menuItem.label} </Menu.Item> ); })}
额外注意
- 别忘了给
useEffect添加依赖项[appWindow.label],避免组件无限重渲染。 - 确保图标组件的导入路径正确。
内容的提问来源于stack exchange,提问作者Hellagur
相关产品推荐
相关产品推荐

