Suponiendo que tiene el siguiente componente que acepta uno o más JSX.Elements secundarios y pasa una devolución de llamada adicional a sus accesorios cuando se representan mediante React.cloneElement(child, { onCancel: () => {} } .
Componente en cuestión (extracto):
interface XComponentProps { onCancel: (index: number) => void; } class XComponent extends React.Component<XComponentProps> { render() { const { onCancel } = this.props; const children = typeof this.props.children === 'function' ? React.cloneElement(this.props.children, { onCancel: () => onCancel() }) : this.props.children.map((child, idx) => ( React.cloneElement(child, { onCancel: () => onCancel(idx) }) )); return <div>{children}</div>; } }Componente en cuestión en acción (extracto):
interface UserComponentProps { caption: string } const UserComponent = (props: UserComponentProps) => ( <button onClick={props.onClose}>{props.caption || "close"}</button> ); ReactDOM.render( <XComponent onCancel={handleCancel}> <UserComponent /> <UserComponent /> <UserComponent /> </XComponent> ); En este momento, TSC se queja de que UserComponent no tiene onCancel en la definición de interfaz de sus accesorios, que de hecho no lo tiene. Una solución más fácil es definir manualmente onCancel en la interfaz UserComponentProps .
Sin embargo, quiero solucionarlo sin modificar la definición de prop del nodo secundario para que el componente pueda aceptar un conjunto arbitrario de elementos React. En tal escenario, ¿hay alguna forma de definir tipos de elementos devueltos que tengan accesorios implícitos adicionales pasados en el XComponent (principal)?
No es posible. No hay forma de saber estáticamente qué accesorios recibe UserComponent del XComponent principal en su contexto ReactDOM.render.
Si desea una solución de tipo seguro, use niños como funciones:
Aquí está la definición XComponent
interface XComponentProps { onCancel: (index: number) => void; childrenAsFunction: (props: { onCancel: (index: number) => void }) => JSX.Element; } class XComponent extends React.Component<XComponentProps> { render() { const { onCancel } = this.props; return <div>{childrenAsFunction({ onCancel })}</div>; } } Ahora puede usarlo para representar sus UserComponent s
<XComponent onCancel={handleCancel} childrenAsFunction={props => <span> <UserComponent {...props} /> <UserComponent {...props} /> </span> } /> Esto funcionará muy bien, uso este patrón a menudo sin problemas. Puede refactorizar XComponentProps para escribir el accesorio childrenAsFunction con la parte relevante (la función onCancel aquí).
Sé que esto se ha solucionado, pero otra solución es restringir el tipo de niños que se pueden pasar. Limita la funcionalidad del contenedor, pero podría ser lo que desea.
Entonces, si mantuvo su ejemplo original:
interface XComponentProps { onCancel: (index: number) => void; children: ReactElement | ReactElement[] } class XComponent extends React.Component<XComponentProps> { render() { const { onCancel } = this.props; const children = !Array.isArray(this.props.children) ? React.cloneElement(this.props.children, { onCancel: () => onCancel() }) : this.props.children.map((child, idx) => ( React.cloneElement(child, { onCancel: () => onCancel(idx) }) )); return <div>{children}</div>; } } interface UserComponentProps { caption: string } const UserComponent: React.FC = (props: UserComponentProps) => ( <button onClick={props.onClose}>{props.caption || "close"}</button> ); ReactDOM.render( <XComponent onCancel={handleCancel}> <UserComponent /> <UserComponent /> <UserComponent /> </XComponent> );Puede pasar hijos como una función, incluso en JSX. De esta manera obtendrá una escritura adecuada todo el tiempo. La interfaz de accesorios de UserComponents debe extender ChildProps.
interface ChildProps { onCancel: (index: number) => void; } interface XComponentProps { onCancel: (index: number) => void; children: (props: ChildProps) => React.ReactElement<any>; } class XComponent extends React.Component<XComponentProps> { render() { const { onCancel } = this.props; return children({ onCancel })}; } } <XComponent onCancel={handleCancel}> { props => <UserComponent {...props} /> } </XComponent>