修复:JSX元素类型无构造或调用签名问题
解决React TypeScript中动态创建MUI图标组件的类型错误
问题场景
你在React TypeScript项目中尝试通过Map动态获取MUI图标组件并渲染,相关代码及报错如下:
按钮配置数组
const generalButtons: MenuButton[] = [ { text: "New Client", onClickFunction: addNewClient, icon: "InfoIcon" }, { text: "New Product", onClickFunction: addNewClient, icon: "AddShoppingCartIcon" }, { text: "New Gift", onClickFunction: addNewClient, icon: "CardGiftcardIcon" } ]
图标映射Map
import InfoIcon from '@mui/icons-material/Info'; import AddShoppingCartIcon from '@mui/icons-material/AddShoppingCart'; import CardGiftcardIcon from '@mui/icons-material/CardGiftcard'; import { OverridableComponent } from '@mui/material/OverridableComponent'; import { SvgIconTypeMap } from '@mui/material'; const menuIconToComponentMap: Map<string, OverridableComponent<SvgIconTypeMap<{}, "svg">> & { muiName: string; }> = new Map(); menuIconToComponentMap.set("InfoIcon", InfoIcon); menuIconToComponentMap.set("AddShoppingCartIcon", AddShoppingCartIcon); menuIconToComponentMap.set("CardGiftcardIcon", CardGiftcardIcon); export default menuIconToComponentMap;
渲染代码及报错
<List> {generalButtons.map((button, index) => { const MenuIcon = menuIconToComponentMap.get(button.icon) return ( <ListItem key={button.text} disablePadding> <ListItemButton onClick={(e) => button.onClickFunction(e)}> <ListItemIcon> <MenuIcon /> </ListItemIcon> <ListItemText primary={button.text} /> </ListItemButton> </ListItem> ) })} </List>
报错信息:JSX element type 'MenuIcon' does not have any construct or call signatures
问题原因
TypeScript无法确定menuIconToComponentMap.get(button.icon)的返回值一定是有效的React组件——因为Map的get方法可能返回undefined,当TypeScript推断出这种可能性时,就会认为MenuIcon不具备组件的构造/调用签名。
解决方案
步骤1:约束图标键的类型(可选但推荐)
先给MenuButton的icon字段定义精确的联合类型,确保所有配置的图标名称都存在于Map的键中:
// 定义所有可用的图标键 type MenuIconKey = "InfoIcon" | "AddShoppingCartIcon" | "CardGiftcardIcon"; // 修正MenuButton接口 interface MenuButton { text: string; onClickFunction: (e: React.MouseEvent) => void; icon: MenuIconKey; }
步骤2:确保组件存在后再渲染
在渲染时添加非空检查,或者使用非空断言(更推荐前者,避免潜在的运行时错误):
方案一:安全检查(推荐)
<List> {generalButtons.map((button) => { const MenuIcon = menuIconToComponentMap.get(button.icon); // 若图标不存在,返回null避免渲染错误 if (!MenuIcon) return null; return ( <ListItem key={button.text} disablePadding> <ListItemButton onClick={button.onClickFunction}> <ListItemIcon> <MenuIcon /> </ListItemIcon> <ListItemText primary={button.text} /> </ListItemButton> </ListItem> ) })} </List>
方案二:非空断言(仅当确定所有图标都在Map中时使用)
<List> {generalButtons.map((button) => { // 用!断言值不为undefined const MenuIcon = menuIconToComponentMap.get(button.icon)!; return ( <ListItem key={button.text} disablePadding> <ListItemButton onClick={button.onClickFunction}> <ListItemIcon> <MenuIcon /> </ListItemIcon> <ListItemText primary={button.text} /> </ListItemButton> </ListItem> ) })} </List>
这样就能让TypeScript确认MenuIcon是有效的React组件,消除类型错误。
内容的提问来源于stack exchange,提问作者András Krausz
相关产品推荐
相关产品推荐

