在React和Typescript中仅允许将特定组件作为子组件

nei*_*iya 5 typing typescript reactjs

我只允许将特定组件作为子组件。例如,假设我有一个Menu组件,该组件仅应包含MenuItem子元素,如下所示:

<Menu>
  <MenuItem />
  <MenuItem />
</Menu>
Run Code Online (Sandbox Code Playgroud)

因此,当我尝试将另一个组件作为子组件时,我希望Typescript在IDE中向我抛出错误。警告我只能MenuItem当孩子的东西。例如在这种情况下:

<Menu>
  <div>My item</div>
  <div>My item</div>
</Menu>
Run Code Online (Sandbox Code Playgroud)

该线程几乎相似,但不包括TypeScript解决方案。我想知道是否可以使用TypeScript类型和接口解决问题。在我的想象中,它看起来像这样,但是类型检查当然不起作用,因为子组件具有Element类型:

type MenuItemType = typeof MenuItem;

interface IMenu {
  children: MenuItemType[];
}

const MenuItem: React.FunctionComponent<IMenuItem> = ({ props }) => {
  return (...)
}

const Menu: React.FunctionComponent<IMenu> = ({ props }) => {
  return (
    <nav>
      {props.children}
    </nav>
  )
}

const App: React.FunctionComponent<IApp> = ({ props }) => {
  return (
    <Menu>
      <MenuItem />
      <MenuItem />
    </Menu>
  )
}
Run Code Online (Sandbox Code Playgroud)

有没有办法用Typescript做到这一点?是否想用仅与特定组件相关的东西扩展Element类型?

还是一种确保孩子是特定组件实例的好方法?无需添加查看子组件displayName的条件。

T.J*_*der 15

尽管有上面的答案,但你不能对孩子这样做。您可以在组件的开发版本中执行运行时检查,但至少现在还不能使用 TypeScript types\xc2\xa0\xe2\x80\x94 执行此操作。

\n

来自 TypeScript 问题#18357

\n
\n

ElementChildrenAttribute现在除了指定内部命名空间之外,没有办法指定子表示是什么JSX。它与子项的 React 表示紧密结合,这意味着子项是 props 的一部分。这使得无法对具有单独存储子项的实现的子项启用类型检查,例如https://github.com/dfilatov/vidom/wiki/Component-properties

\n
\n

请注意, #21699中引用了它,基本上,周围可能发生的破坏性更改ReactElement也可能使执行此操作成为可能。

\n

现在,您所能做的就是运行时检查,或接受 props (或 props 数组)和可选的组件函数(在您的情况下,您知道它的MenuItem)并在组件中创建元素。

\n

还有一个问题是你是否应该这样做。为什么我不能编写一个返回 a 的组件MenuItem而不是MenuItem直接使用?:-)

\n


Min*_*ork 13

为此,您需要从子组件(最好也是父组件)中提取 props 接口并以这种方式使用它:

interface ParentProps {
    children: ReactElement<ChildrenProps> | Array<ReactElement<ChildrenProps>>;
}

Run Code Online (Sandbox Code Playgroud)

所以在你的情况下,它看起来像这样:

interface IMenu {
  children: ReactElement<IMenuItem> | Array<ReactElement<IMenuItem>>;
}

const MenuItem: React.FunctionComponent<IMenuItem> = ({ props }) => {
  return (...)
}

const Menu: React.FunctionComponent<IMenu> = ({ props }) => {
  return (
    <nav>
      {props.children}
    </nav>
  )
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的解决方案,效果很好!是否可以检查它是否只包含这些类型的子代?例如,如果有一个 MenuItem 组件和一个 div 标签,则不会显示错误。 (5认同)
  • 我和@Salem 一起讨论这个问题。如果您有一个只能将“RedButton”作为子组件的“RedButtonContainer”组件怎么办?如果“RedButton”采用与“BlueButton”完全相同的属性,它们将具有相同的类型(就 TypeScript 而言),即使按钮显示为蓝色而不是红色。`RedButton` 和 `BlueButton` 只是同一事物的别名,对吧? (4认同)
  • 我其实不知道这个答案是否正确。我不知道在“children”上添加什么检查这些类型,但它们似乎只是确保子项存在,而不是确保它是特定类型。问题归结为这样一个事实:任何呈现 JSX 的内容都会返回“JSX.Element”,它是通用的,不够具体,无法进行比较。TypeScript github 上有此问题的更多详细信息:https://github.com/microsoft/TypeScript/issues/21699 (2认同)