如何使用 TypeScript 2.3 中新增的支持来限制 TypeScript 中 React Children 的类型?

IT技术 reactjs typescript
2021-04-14 02:47:05

我正在尝试利用最近添加的对 TypeScript 编译器和 @types/react中儿童输入的支持,但很挣扎。我正在使用 TypeScript 2.3.4 版。

假设我有这样的代码:

interface TabbedViewProps {children?: Tab[]}
export class TabbedView extends React.Component<TabbedViewProps, undefined> {

  render(): JSX.Element {
    return <div>TabbedView</div>;
  }
}

interface TabProps {name: string}
export class Tab extends React.Component<TabProps, undefined> {
  render(): JSX.Element {
    return <div>Tab</div>
  }
}

当我尝试像这样使用这些组件时:

return <TabbedView>
  <Tab name="Creatures">
    <div>Creatures!</div>
  </Tab>
  <Tab name="Combat">
    <div>Combat!</div>
  </Tab>
</TabbedView>;

我收到如下错误:

ERROR in ./src/typescript/PlayerView.tsx
(27,12): error TS2322: Type '{ children: Element[]; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<TabbedView> & Readonly<{ children?: ReactNode; }> ...'.
  Type '{ children: Element[]; }' is not assignable to type 'Readonly<TabbedViewProps>'.
    Types of property 'children' are incompatible.
      Type 'Element[]' is not assignable to type 'Tab[] | undefined'.
        Type 'Element[]' is not assignable to type 'Tab[]'.
          Type 'Element' is not assignable to type 'Tab'.
            Property 'render' is missing in type 'Element'.

它似乎在推断孩子的类型,Element[]而不是Tab[]即使这是我使用的唯一类型的孩子。

编辑:限制子props的接口而不是直接限制子组件的类型也可以,因为我需要做的就是从子组件中提取一些特定的props。

4个回答

编辑 2:原来这种方法可以防止警告,但根据评论TabProps没有正确检查。

您应该尝试像这样设置界面 TabbedViewProps 的子项

interface TabbedViewProps { children?: React.ReactElement<TabProps>[] }

这里的想法不是告诉你TabbedView有一个数组Tab,而是告诉你TabbedView他有一个element需要特定props的数组在你的情况下TabProps

编辑(感谢 Matei):

interface TabbedViewProps {
    children?: React.ReactElement<TabProps>[] | React.ReactElement<TabProps>
}
看起来用 ReactElement 包裹 props 会放松类型,以便实际上允许任何元素。这是我将鼠标悬停在我的客户元素上时得到的结果:type ChartElement<T> = ReactElement<ChartProps<T>, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)> 注意|(props: any) =>.
2021-05-24 02:47:05
我有一个类似的问题。就我而言,我像您一样定义了子元素的类型(即 TabProps),但是当我将元素作为 TabbedView 的子元素放置时,它的形状不像 TabProps,typescript不会报告任何错误或警告。你能提供一些建议或线索吗?
2021-05-29 02:47:05
我遵循类似的方法:children?: Array<React.ReactChild> | React.ReactChild. 通过这种方式,组件甚至可以接受单个子项,而不必接受子项数组。
2021-06-11 02:47:05
与@Noah 相同的问题。似乎您只能在一定程度上限制子元素,但是使用某些道具限制子元素是行不通的。它会确保它是一个元素,但道具并不重要。无赖!
2021-06-14 02:47:05
我面临同样的问题。即使我children像上面那样定义了类型,TypeScript 也不会检查孩子的类型。
2021-06-21 02:47:05

已经作为指针,声明TabbedView.children为:

children: React.ReactElement<TabProps> | React.ReactElement<TabProps>[];

将摆脱错误,但不会正确地对孩子进行类型检查。也就是说,您仍然可以传递除TabPropsto以外的子项TabbedView而不会出现任何错误,因此这也是有效的:

return (
  <TabbedView>
    <Tab name="Creatures">
      <div>Creatures!</div>
    </Tab>

    <Tab name="Combat">
        <div>Combat!</div>
    </Tab>

    <NotTabButValidToo />
  </TabbedView>
);

你可以做的是声明一个props,比方说tabs: TabProps[],传递创建这些Tabs所需的props,而不是它们的 JSX,并将它们呈现在里面TabbedView

interface TabbedViewProps {
  children?: never;
  tabs?: TabProps[];
}

...

const TabbedView: React.FC<TabbedViewProps > = ({ tabs }) => {
  return (
    ...

    { tabs.map(tab => <Tab key={ ... } { ...tab } />) }

    ...
  );
};
已经研究了一段时间,我认为这是考虑到儿童局限性的最佳方法
2021-05-23 02:47:05
仅仅因为类型检查器的限制而选择更糟糕的 API 对我来说似乎不是一个明智的决定。
2021-06-08 02:47:05

我试图断言类型。您可以抛出或忽略。

interface TabbedViewProps {
  children?: React.ReactElement<ITabProps> | React.ReactElement<ITabProps>[]
}

并且在组件本身映射子项并断言或忽略

{React.Children.map(props.children, (tab) => {
  if(tab?.type != Tab) return;
  console.log(tab?.type == Tab);
  return tab;
})}
什么是“标签” ab?.type != Tab
2021-06-13 02:47:05

您在 Tab 渲染方法中返回的类型是 JSX.Element。这就是导致您出现问题的原因。TabbedView 期待具有 Tab 类型的儿童数组。我不确定您是否可以将某个组件指定为子类型。它可以是字符串或 JSX.Element。你能显示 Tab 的定义文件吗?

查看https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts以了解 JSX.Element 接口的外观。

编辑:仍然无法正常工作。我尝试了 React.ReactElement<TabProps>,它似乎有效,但没有正确拒绝子组件中的非 Tab 组件。
2021-05-23 02:47:05
我不太确定您要什么,对不起:( 这是我的 tsx 项目中与 Tab 相关的全部代码。我很乐意尝试提供您所要求的内容,但我我想我需要一点帮助。
2021-05-25 02:47:05
您已经展示了组件的定义。不是接口。
2021-05-28 02:47:05
您正在使用 react 组件的实例作为类型(接口)。typescriptlang.org/docs/handbook/interfaces.html
2021-06-08 02:47:05
我在第一个代码块中展示了 TabbedView 和 Tab 的完整定义。对儿童类型的新支持的重点似乎是允许限制组件(如果您看到它应该解决的问题链接在 PR 顶部),所以我想我一定遗漏了一些东西。
2021-06-14 02:47:05