Javascript 键入一个React组件,该组件克隆其子组件以在TypeScript中添加额外的道具

Javascript 键入一个React组件,该组件克隆其子组件以在TypeScript中添加额外的道具,javascript,reactjs,typescript,typescript-typings,Javascript,Reactjs,Typescript,Typescript Typings,假设您有以下组件,它接受一个或多个子JSX.element,并在使用React.cloneElement(child,{onCancel:()=>{}呈现它们时,将额外的回调传递给它们的道具 相关组件(节选): interface XComponentProps { onCancel: (index: number) => void; } class XComponent extends React.Component<XComponentProps> { r

假设您有以下组件,它接受一个或多个子JSX.element,并在使用
React.cloneElement(child,{onCancel:()=>{}
呈现它们时,将额外的回调传递给它们的道具

相关组件(节选):

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>;
    }
}
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>
);
接口XComponentProps{
onCancel:(索引:编号)=>无效;
}
类XComponent扩展了React.Component{
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(子,{onCancel:()=>onCancel(idx)})
));
返回{children};
}
}
正在运行的组件(摘录):

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>;
    }
}
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>
);
interface UserComponentProps{caption:string}
const UserComponent=(props:UserComponentProps)=>(
{props.caption | |“close”}
);
ReactDOM.render(
);
目前TSC抱怨其道具的接口定义中没有
onCancel
,而实际上没有。最简单的解决方法是手动将
onCancel
定义为
UserComponentProps
接口


但是,我想在不修改子节点的prop定义的情况下修复它,这样组件就可以接受任意一组React元素。在这种情况下,有没有办法定义返回元素的类型,这些元素在
XComponent
(父)级别传递了额外的隐式prop?

您可以使用接口继承(请参阅)并让UserComponentProps扩展XComponentProps:

接口UserComponentProps扩展了XComponentProps{caption:string}

这将为UserComponentProps提供XComponentProps自身属性之外的所有属性

如果不希望要求UserComponentProps定义XComponentProps的所有属性,还可以使用分部类型(请参阅):

interface UserComponentProps扩展部分{caption:string}


这将为UserComponentProps提供XComponentProps的所有属性,但使其成为可选属性。

这是不可能的。无法静态地知道在ReactDOM.render上下文中,props UserComponent从父XComponent接收到什么

如果需要类型安全解决方案,请将子项用作函数:

下面是
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>;
    }
}
这将很好地工作,我经常使用这种模式,没有任何麻烦。
您可以重构XComponentProps以键入带有相关部分的
childrenAsFunction
道具(此处为
onCancel
函数)。

您可以将子函数作为函数传递,即使在JSX中也是如此。这样,您就可以一直正确地键入。 UserComponents道具接口应扩展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>
接口子道具{
onCancel:(索引:编号)=>无效;
}
接口XComponentProps{
onCancel:(索引:编号)=>无效;
children:(props:ChildProps)=>React.ReactElement;
}
类XComponent扩展了React.Component{
render(){
const{onCancel}=this.props;
返回子项({onCancel}});
}
}
{props=>}

我知道这已经解决了,但另一个解决方案是限制可以传递的子项的类型。限制包装器的功能,但可能是您想要的

因此,如果您保留您最初的示例:

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>
);
接口XComponentProps{
onCancel:(索引:编号)=>无效;
子项:ReactElement | ReactElement[]
}
类XComponent扩展了React.Component{
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(子,{onCancel:()=>onCancel(idx)})
));
返回{children};
}
}
接口UserComponentProps{caption:string}
const UserComponent:React.FC=(props:UserComponentProps)=>(
{props.caption | |“close”}
);
ReactDOM.render(
);

Thank@erikamjoh,这是我目前的解决方案,但我希望在不修改子组件道具的情况下实现它……感谢有趣的解决方案!作为函数传递是有意义的,这样TSC就可以知道父组件和子组件之间的关系。