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,和联合类型的分支要求冲突。
具体修复步骤
- 修正类型定义笔误:
PreIdToastInfo中的PreIdBasicToast应为你定义的BasicToast,否则类型关联会出错。 - 手动收窄组件调用时的类型:
通过条件判断区分分支,让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} /> ); } })} - 优化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); }); - 优化可选属性定义:
将ToastWithoutButton中的hasButton?: never改为hasButton?: false,让布尔值的区分更清晰,帮助TS更容易收窄类型:interface ToastWithoutButton { hasButton?: false; buttonMessage?: never; handleButtonClick?: never; dismissOnButtonClick?: never; }
内容的提问来源于stack exchange,提问作者bit07123
相关产品推荐
相关产品推荐

