【问题标题】:Only allow specific components as children in React and Typescript在 React 和 Typescript 中只允许特定组件作为子组件
【发布时间】:2019-08-23 14:03:42
【问题描述】:

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

<Menu>
  <MenuItem />
  <MenuItem />
</Menu>

所以当我尝试将另一个组件作为子组件时,我希望 Typescript 在 IDE 中给我一个错误。有些东西警告我,我应该只使用MenuItem 作为孩子。例如在这种情况下:

<Menu>
  <div>My item</div>
  <div>My item</div>
</Menu>

This thread 几乎相似,但不包括 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>
  )
}

有没有办法通过 Typescript 实现这一点?喜欢用仅与特定组件相关的东西来扩展 Element 类型?

或者,什么是确保孩子是特定组件的实例的好方法?无需添加查看子组件 displayName 的条件。

【问题讨论】:

  • 如果子组件扩展了一个类型而不是组件,而该类型扩展了组件加上一个额外的属性怎么办?

标签: reactjs typescript typing


【解决方案1】:

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

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

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

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>
  )
}

【讨论】:

  • 很好的解决方案,效果很好!是否可以检查它是否包含这些类型的only 子项?例如,如果有 MenuItem 组件和 div 标签,则不会显示错误。
  • 我认为这在 typescript 中是不可能的(但我可能错了),因为它无法动态解析嵌套在某个组件中的组件应该匹配在该组件子属性中声明的类型。
  • 据我记得,尽管在 jsx 中使用 PropTypes 在技术上是可行的,但它需要一个讨厌的 hack 来提取组件名称(可能并不总是存在)并将其与子组件名称进行比较。
  • 我其实不知道这个答案是否正确。我不知道在children add 上检查这些类型是什么,但它们似乎只确保存在一个孩子,而不是确保它属于特定类型。问题归结为任何渲染 JSX 的东西都会返回 JSX.Element,这是通用的,不够具体,无法比较。 TypeScript github上有这个问题的更多细节:github.com/microsoft/TypeScript/issues/21699
  • 我和@Salem 一起做这个。如果你有一个 RedButtonContainer 组件只能将 RedButtons 作为子级怎么办?如果 RedButton 采用与 BlueButton 完全相同的道具,它们将具有相同的类型(就 TypeScript 而言),即使按钮显示为蓝色而不是红色。 RedButtonBlueButton 只是同一事物的别名,对吧?
猜你喜欢
  • 1970-01-01
  • 2021-11-03
  • 2022-01-23
  • 2020-04-10
  • 2015-02-06
  • 1970-01-01
  • 1970-01-01
  • 2023-01-24
相关资源
最近更新 更多