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

TypeScript中Prop类型匹配但React组件仍报错问题排查

问题:TypeScript Toast组件类型不匹配错误

问题场景

我从JavaScript转向TypeScript,在给组件添加类型定义时遇到一个问题:在ToastsList.tsx中调用Toast.tsx组件,每个属性和传入值的类型看起来都匹配,但<Toast>组件名称下出现红线报错。

ToastsList.tsx中的调用代码:

{toasts?.length > 0 &&
        toasts.map((toast) => (
          <Toast
            title={toast.title}
            id={toast.id}
            message={toast.message}
            type={toast.type}
            hasButton={toast.hasButton}
            buttonMessage={toast.buttonMessage}
            handleButtonClick={toast.handleButtonClick}
            dismissOnButtonClick={toast.dismissOnButtonClick}
            hasUrl={toast.hasUrl}
            url={toast.url}
            urlMsg={toast.urlMsg}
          />
        ))}

报错信息

Type '{ title: string; id: number; message: string; type: string; hasButton: true | undefined; buttonMessage: string | undefined; handleButtonClick: (() => void) | undefined; dismissOnButtonClick: boolean | undefined; hasUrl: boolean | undefined; url: string | undefined; urlMsg: string | undefined; }' is not assignable to type 'IntrinsicAttributes & ToastInfo'.
  
Type '{ title: string; id: number; message: string; type: string; hasButton: true | undefined; buttonMessage: string | undefined; handleButtonClick: (() => void) | undefined; dismissOnButtonClick: boolean | undefined; hasUrl: boolean | undefined; url: string | undefined; urlMsg: string | undefined; }' is not assignable to type 'ToastWithoutButton'.
    Types of property 'hasButton' are incompatible.
      Type 'true | undefined' is not assignable to type 'undefined'.
        Type 'true' is not assignable to type 'undefined'.

相关代码

Toast.tsx组件定义

function Toast({
  title,
  message,
  type,
  id,
  hasUrl,
  url,
  urlMsg,
  hasButton,
  buttonMessage,
  handleButtonClick,
  dismissOnButtonClick,
}: ToastInfo) 

Context中的toasts更新逻辑

setToasts((toasts) => {
      return [
        ...toasts,
        {
          title,
          type,
          message,
          url,
          urlMsg,
          createdAt: curMilliseconds,
          id: curMilliseconds,
          hasButton,
          buttonMessage,
          handleButtonClick,
          dismissOnButtonClick,
        },
      ].slice(0, MAX_TOASTS_AMOUNT) as ToastInfo[];
    });

类型定义

interface BasicToast {
  title: string;
  message: string;
  type: string;
  id: number;
}

interface ToastWithButton {
  hasButton: true;
  buttonMessage: string;
  handleButtonClick: () => void;
  dismissOnButtonClick: boolean;
}

interface ToastWithoutButton {
  hasButton?: never;
  buttonMessage?: never;
  handleButtonClick?: never;
  dismissOnButtonClick?: never;
}

interface ToastWithURL {
  hasUrl: true;
  url: string;
  urlMsg: string;
}

interface ToastWithoutURL {
  // It might be that hasUrl: false or hasUrl: true is always false when compared to boolean
  hasUrl?: false;
  url?: never;
  urlMsg?: never;
}
export type PreIdToastInfo = PreIdBasicToast &
  (ToastWithButton | ToastWithoutButton) &
  (ToastWithURL | ToastWithoutURL);

解决方案

问题核心是TypeScript无法自动收窄联合类型的分支:虽然你将toasts数组断言为ToastInfo[],但在map遍历的时候,TS无法识别每个toast具体属于ToastWithButton还是ToastWithoutButton分支,导致所有可选属性被推断为类型 | undefined,和联合类型的分支要求冲突。

具体修复步骤

  1. 修正类型定义笔误:PreIdToastInfo中的PreIdBasicToast应为你定义的BasicToast,否则类型关联会出错。
  2. 手动收窄组件调用时的类型:
    通过条件判断区分分支,让TS自动识别具体类型:
    {toasts?.length > 0 &&
      toasts.map((toast) => {
        if (toast.hasButton) {
          return (
            <Toast
              key={toast.id}
              title={toast.title}
              id={toast.id}
              message={toast.message}
              type={toast.type}
              hasButton={toast.hasButton}
              buttonMessage={toast.buttonMessage}
              handleButtonClick={toast.handleButtonClick}
              dismissOnButtonClick={toast.dismissOnButtonClick}
              hasUrl={toast.hasUrl}
              url={toast.url}
              urlMsg={toast.urlMsg}
            />
          );
        } else {
          return (
            <Toast
              key={toast.id}
              title={toast.title}
              id={toast.id}
              message={toast.message}
              type={toast.type}
              hasUrl={toast.hasUrl}
              url={toast.url}
              urlMsg={toast.urlMsg}
            />
          );
        }
      })}
    
  3. 优化Context的类型断言:
    不要直接在数组上断言类型,而是确保新创建的toast本身符合ToastInfo类型:
    const newToast = {
      title,
      type,
      message,
      url,
      urlMsg,
      createdAt: curMilliseconds,
      id: curMilliseconds,
      hasButton,
      buttonMessage,
      handleButtonClick,
      dismissOnButtonClick,
    } as ToastInfo;
    
    setToasts((toasts) => {
      return [...toasts, newToast].slice(0, MAX_TOASTS_AMOUNT);
    });
    
  4. 优化可选属性定义:
    将ToastWithoutButton中的hasButton?: never改为hasButton?: false,让布尔值的区分更清晰,帮助TS更容易收窄类型:
    interface ToastWithoutButton {
      hasButton?: false;
      buttonMessage?: never;
      handleButtonClick?: never;
      dismissOnButtonClick?: never;
    }
    

内容的提问来源于stack exchange,提问作者bit07123

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 02:29:58