You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 05:15:42