React TypeScript报错:类型'number'不存在属性'X'的问题咨询
解决TypeScript中加载/非加载状态数组遍历的类型错误
方案1:拆分渲染逻辑(最推荐)
把加载和非加载状态的渲染逻辑分开,让TypeScript能准确推断各自的数组类型,代码也更清晰易维护。
{loading ? ( Array.from(Array(6).keys()).map((_, i) => ( <Fragment key={i}> <Accordion TransitionProps={{ mountOnEnter: true, unmountOnExit: true }} className={classes.expPanel} expanded={expanded === `panel${i + 1}`} onChange={handleChange(`panel${i + 1}`)} > <AccordionSummary className={classes.expPanelSummary} expandIcon={<ExpandMore />} aria-controls="panel1d-content" id="panel1d-header" > <ListItemIcon className={classes.brandIcon}> {renderSkeleton(28, 28, 'circle')} </ListItemIcon> {renderSkeleton()} </AccordionSummary> <AccordionDetails className={classes.expPanelDetails}> {/* 这里可以对应Sites组件的占位骨架屏 */} {renderSkeleton(200, 100)} </AccordionDetails> </Accordion> {i < 5 && <Divider />} </Fragment> )) ) : ( brands.map((brand, i, arr) => ( <Fragment key={i}> <Accordion TransitionProps={{ mountOnEnter: true, unmountOnExit: true }} className={classes.expPanel} expanded={expanded === `panel${i + 1}`} onChange={handleChange(`panel${i + 1}`)} > <AccordionSummary className={classes.expPanelSummary} expandIcon={<ExpandMore />} aria-controls="panel1d-content" id="panel1d-header" > <ListItemIcon className={classes.brandIcon}> <Icon>{brand.icon}</Icon> </ListItemIcon> <Typography>{brand.name}</Typography> </AccordionSummary> <AccordionDetails className={classes.expPanelDetails}> <Sites sites={brand?.sites} readOnly={readOnly} /> </AccordionDetails> </Accordion> {i < arr.length - 1 && <Divider />} </Fragment> )) )}
方案2:使用类型断言
如果不想拆分逻辑,可在访问品牌属性时用类型断言,明确告诉TypeScript当前item的类型。注意要确保loading状态和数组类型严格对应,避免运行时错误。
{(loading ? Array.from(Array(6).keys()) : brands).map((item, i, arr) => ( <Fragment key={i}> <Accordion TransitionProps={{ mountOnEnter: true, unmountOnExit: true }} className={classes.expPanel} expanded={expanded === `panel${i + 1}`} onChange={handleChange(`panel${i + 1}`)} > <AccordionSummary className={classes.expPanelSummary} expandIcon={<ExpandMore />} aria-controls="panel1d-content" id="panel1d-header" > <ListItemIcon className={classes.brandIcon}> {loading ? renderSkeleton(28, 28, 'circle') : <Icon>{(item as Brand).icon}</Icon>} </ListItemIcon> {loading ? renderSkeleton() : <Typography>{(item as Brand).name}</Typography>} </AccordionSummary> <AccordionDetails className={classes.expPanelDetails}> {loading ? renderSkeleton(200, 100) : <Sites sites={(item as Brand)?.sites} readOnly={readOnly} />} </AccordionDetails> </Accordion> {i < arr.length - 1 && <Divider />} </Fragment> ))}
方案3:自定义类型守卫
写一个类型守卫函数,帮助TypeScript判断当前item是否为品牌类型,实现更安全的类型推断。
首先定义类型守卫:
function isBrand(item: number | Brand): item is Brand { return typeof item !== 'number'; }
然后在渲染中使用:
{(loading ? Array.from(Array(6).keys()) : brands).map((item, i, arr) => ( <Fragment key={i}> <Accordion TransitionProps={{ mountOnEnter: true, unmountOnExit: true }} className={classes.expPanel} expanded={expanded === `panel${i + 1}`} onChange={handleChange(`panel${i + 1}`)} > <AccordionSummary className={classes.expPanelSummary} expandIcon={<ExpandMore />} aria-controls="panel1d-content" id="panel1d-header" > <ListItemIcon className={classes.brandIcon}> {loading ? renderSkeleton(28, 28, 'circle') : isBrand(item) && <Icon>{item.icon}</Icon>} </ListItemIcon> {loading ? renderSkeleton() : isBrand(item) && <Typography>{item.name}</Typography>} </AccordionSummary> <AccordionDetails className={classes.expPanelDetails}> {loading ? renderSkeleton(200, 100) : isBrand(item) && <Sites sites={item?.sites} readOnly={readOnly} />} </AccordionDetails> </Accordion> {i < arr.length - 1 && <Divider />} </Fragment> ))}
内容的提问来源于stack exchange,提问作者Mohammed Mounir
相关产品推荐
相关产品推荐

