TypeScript如何约束React Modal仅接收指定子组件作为顶层子元素
解决方案
你之前直接将子组件返回值定义为ReactElement的方案无法生效,原因是所有JSX元素的返回值都属于ReactElement类型,<div />这类原生元素也满足该类型定义,TS无法从类型层面区分普通元素和你自定义的Header/Body/Footer元素。可以通过添加TS独有品牌标记的方式实现约束,也可以额外补充运行时校验适配非TS使用场景。
完整实现代码
import React, { ReactElement, ReactNode } from 'react' // 定义带独有品牌标记的子组件类型(仅TS层面生效,不影响运行时代码) type ModalHeaderElement = ReactElement & { __brand: 'ModalHeader' } type ModalBodyElement = ReactElement & { __brand: 'ModalBody' } type ModalFooterElement = ReactElement & { __brand: 'ModalFooter' } // 子组件类型定义 function Header(props: { children: ReactNode }): ModalHeaderElement { const { children } = props // 用类型断言添加虚拟品牌标记 return <>{children}</> as ModalHeaderElement } function Body(props: { children: ReactNode }): ModalBodyElement { const { children } = props return <>{children}</> as ModalBodyElement } function Footer(props: { children: ReactNode }): ModalFooterElement { const { children } = props return <>{children}</> as ModalFooterElement } // Modal仅接受三个子组件作为顶层子元素,支持单个或多个子元素 type ModalProps = { children: ModalHeaderElement | ModalBodyElement | ModalFooterElement | Array<ModalHeaderElement | ModalBodyElement | ModalFooterElement> } function Modal(props: ModalProps) { const { children } = props // 可选:开发环境添加运行时校验,适配非TS使用场景 if (process.env.NODE_ENV === 'development') { const allowedComponents = [Header, Body, Footer] React.Children.forEach(children, child => { if (!allowedComponents.includes((child as ReactElement).type as any)) { throw new Error('Modal 仅允许顶层子元素为 Modal.Header、Modal.Body、Modal.Footer') } }) } return <>{children}</> } Modal.Header = Header Modal.Body = Body Modal.Footer = Footer export default Modal
效果说明
- 编译时:如果传入
<div />等非指定组件作为Modal的直接子元素,TS会直接抛出类型错误 - 运行时(开发环境):如果用户绕过TS校验传入非法子元素,会在控制台抛出明确的错误提示
内容的提问来源于stack exchange,提问作者Alexander Zeitler
相关产品推荐
相关产品推荐

