如何键入可能有道具的 ReactNode?

How to type ReactNode that may have props?

我已经为此苦苦思索了几个小时,但没有成功。似乎找不到任何其他有帮助的 Whosebug 问题。基本上,我有一个包含两个子节点的组件,如下所示:

<SharedTwoColumns
  outer={{ className: "mt4 mb4" }}
  gap={<div className="h4 w4" />}
>
  <div className={isMobile ? "" : "pa3"}>Left Column</div> // first child
  <div className={isMobile ? "" : "pa3"}>Right Column</div> // second child
</SharedTwoColumns>    

我正在尝试以某种方式键入“左栏”和“右栏”子级,表明它们都可能有道具,但不一定。我到目前为止是:

const SharedTwoColumns = ({
  outer: { className: ocn, ...outer },
  children,
  gap: {
    props: { className: gcn, ...gapProps }
  }
}: {
  outer: { [x: string]: unknown; className: string };
  children: ReactNode;
  gap: { props: { [x: string]: unknown; className: string } };
}) => {

...

  const [leftColumn, rightColumn]: ReactNode[] = Children.toArray(children);
  const {
    props: { className: lcn, children: leftColumnChildren, ...leftColumnProps }
  }: ReactNode = leftColumn || {}; // TS error happens here when I hover over 'props'

但是,这给了我打字稿错误:

Property 'props' does not exist on type 'boolean | ReactChild | ReactFragment | ReactPortal | null'.

any

有人知道我在解构 leftColumn 的 props 时如何正确输入吗?

不确定这是否回答了您的问题,但您可以创建一个扩展 ReactNode class 的接口。 例如

interface MyNode extends ReactNode {
  // ... your custom props here
}

通过这种方式,我相信您可以使用您的自定义界面来输入您的 Columns,在某种程度上,它们拥有 ReactNode 的所有原生 props 加上您的自定义 props

如果我理解正确,您需要为 children 使用 ReactElement 类型,例如:

import React, { ReactElement } from "react";

interface LeftProps {
  left: string;
}
const Left = (props: LeftProps) => null;

interface RightProps {
  right: string;
}
const Right = (props: RightProps) => null;

interface LayoutProps {
  children: [ReactElement<LeftProps>, ReactElement<RightProps>];
}

function Layout({ children }: LayoutProps) {
  // You may use React.children.toArray here but it will force you to typecast
  // in some cases (like this one with tuple)
  const left = children[0].props.left;
  const right = children[1].props.right;

  return (
    <div>
      Right: {right} Left: {left}
    </div>
  );
}

export default function App() {
  return (
    <Layout>
      <Left left="left" />
      <Right right="right" />
    </Layout>
  );
}