在 React 和 Typescript 中仅允许特定组件为 children
Only allow specific components as children in React and Typescript
我只想允许特定组件作为 children。例如,假设我有一个菜单组件,它应该只包含 MenuItem
作为 children,像这样:
<Menu>
<MenuItem />
<MenuItem />
</Menu>
所以当我尝试将另一个组件作为 child 时,我希望 Typescript 在 IDE 中抛出一个错误。警告我应该只使用 MenuItem
作为 children。例如在这种情况下:
<Menu>
<div>My item</div>
<div>My item</div>
</Menu>
This thread 几乎相似,但不包括 TypeScript 解决方案。我想知道这个问题是否可以使用 TypeScript 类型和接口来解决。在我想象的世界中,它看起来像这样,但是类型检查当然不起作用,因为 child 组件有一个 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 类型?
或者什么是确定 child 是特定组件实例的好方法?无需添加查看 child 组件 displayName.
的条件
为此,您需要从子组件(最好是父组件)中提取 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>
)
}
尽管上面有答案,但您不能使用 children 来做到这一点。您可以在组件的开发版本中执行运行时检查,但您不能对 TypeScript 类型执行此操作 — 至少现在还不能。
来自 TypeScript 问题 #18357:
Right now there's no way to specify what children representation is, except specifying ElementChildrenAttribute
inside JSX
namespace. It's heavily coupled with React representation for children which implies that children is a part of props. This makes impossible to enable type checking for children with implementations which store children separately, for instance https://github.com/dfilatov/vidom/wiki/Component-properties.
请注意,它在 #21699 中被引用,基本上 possibly-breaking 周围 ReactElement
的变化也可能使它成为可能。
现在,您所能做的就是运行时检查,或接受道具(或道具数组)和可选的组件函数(在您的情况下,您知道它是 MenuItem
)并在其中创建元素你的组件。
还有一个问题是您是否应该这样做。为什么我不能编写 returns 和 MenuItem
的组件,而不必直接使用 MenuItem
? :-)
我只想允许特定组件作为 children。例如,假设我有一个菜单组件,它应该只包含 MenuItem
作为 children,像这样:
<Menu>
<MenuItem />
<MenuItem />
</Menu>
所以当我尝试将另一个组件作为 child 时,我希望 Typescript 在 IDE 中抛出一个错误。警告我应该只使用 MenuItem
作为 children。例如在这种情况下:
<Menu>
<div>My item</div>
<div>My item</div>
</Menu>
This thread 几乎相似,但不包括 TypeScript 解决方案。我想知道这个问题是否可以使用 TypeScript 类型和接口来解决。在我想象的世界中,它看起来像这样,但是类型检查当然不起作用,因为 child 组件有一个 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 类型?
或者什么是确定 child 是特定组件实例的好方法?无需添加查看 child 组件 displayName.
的条件为此,您需要从子组件(最好是父组件)中提取 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>
)
}
尽管上面有答案,但您不能使用 children 来做到这一点。您可以在组件的开发版本中执行运行时检查,但您不能对 TypeScript 类型执行此操作 — 至少现在还不能。
来自 TypeScript 问题 #18357:
Right now there's no way to specify what children representation is, except specifying
ElementChildrenAttribute
insideJSX
namespace. It's heavily coupled with React representation for children which implies that children is a part of props. This makes impossible to enable type checking for children with implementations which store children separately, for instance https://github.com/dfilatov/vidom/wiki/Component-properties.
请注意,它在 #21699 中被引用,基本上 possibly-breaking 周围 ReactElement
的变化也可能使它成为可能。
现在,您所能做的就是运行时检查,或接受道具(或道具数组)和可选的组件函数(在您的情况下,您知道它是 MenuItem
)并在其中创建元素你的组件。
还有一个问题是您是否应该这样做。为什么我不能编写 returns 和 MenuItem
的组件,而不必直接使用 MenuItem
? :-)