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

Chr*_*ong 31 typescript reactjs

我正在尝试利用最近添加的对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>
  }
}
Run Code Online (Sandbox Code Playgroud)

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

return <TabbedView>
  <Tab name="Creatures">
    <div>Creatures!</div>
  </Tab>
  <Tab name="Combat">
    <div>Combat!</div>
  </Tab>
</TabbedView>;
Run Code Online (Sandbox Code Playgroud)

我收到如下错误:

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'.
Run Code Online (Sandbox Code Playgroud)

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

编辑:限制子道具的界面,而不是直接限制子组件的类型也没关系,因为我需要做的就是从子组件中拉出一些特定的道具.

Pie*_*rry 20

您应该尝试像这样设置接口TabbedViewProps的子项

interface TabbedViewProps { children?: React.ReactElement<TabProps>[] }
Run Code Online (Sandbox Code Playgroud)

这里的想法不是告诉你TabProps有一个数组TabbedView,而是告诉你Tab他有一个TabbedView特定道具阵列.在你的情况下element.

编辑(thx到Matei):

interface TabbedViewProps {
    children?: React.ReactElement<TabProps>[] | React.ReactElement<TabProps>
}
Run Code Online (Sandbox Code Playgroud)

编辑2:事实证明这种方法可以防止警告,但根据评论TabProps未正确检查.

  • 我有一个类似的问题.在我的例子中,我像你一样定义了子类型(TabProps),但当我将一个元素放置为TabbedView的子元素时,它的形状不像TabProps,typescript不会报告任何错误或警告.你能提出一些建议或线索吗? (9认同)
  • 看起来用 ReactElement 包装 props 会放松类型,以便实际上允许任何元素。以下是将鼠标悬停在客户元素上时得到的结果:`type ChartElement&lt;T&gt; = ReactElement&lt;ChartProps&lt;T&gt;, string | ((props:any) =&gt; ReactElement&lt;any, string | ... | (new (props:any) =&gt; Component&lt;any,any,any&gt;)&gt; | null) | (new (props:any) =&gt; Component&lt;any,any,any&gt;)&gt;` 请注意 `|(props:any) =&gt;`。 (5认同)
  • 我遵循类似的方法:`children?:Array <React.ReactChild> | React.ReactChild`.这样,组件甚至可以接受单个孩子而不一定是一系列孩子. (4认同)
  • 与@Noah相同。似乎您只能在一定程度上限制子项,但仅使用某些道具*限制子项元素是行不通的。它会确保它是一个元素,但是道具并不重要。mm! (4认同)
  • 我正面临着同样的问题。即使我像上面那样定义`children`类型,TypeScript也不检查子类型。 (2认同)
  • 详细阅读所有评论和答案后,我想明确声明(因为还没有人):**在撰写本文时这是不可能的**。也就是说,如果传递了错误类型的子级,则不能引发错误,这就是问题所要求的。 (2认同)

Dan*_*ger 15

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

children: React.ReactElement<TabProps> | React.ReactElement<TabProps>[];
Run Code Online (Sandbox Code Playgroud)

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

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

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

    <NotTabButValidToo />
  </TabbedView>
);
Run Code Online (Sandbox Code Playgroud)

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

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

...

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

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

    ...
  );
};
Run Code Online (Sandbox Code Playgroud)

  • 仅仅因为类型检查器的限制而选择更糟糕的 API 对我来说似乎不是一个明智的决定。 (23认同)
  • IMO 这不是一个更差的 api,而是一个更好的 api。仅仅因为它可能不太容易打字并不意味着它客观上“更糟糕”。这个api更清楚地表达了想要的内容。我们需要一个选项卡列表(这是打字明确说明的内容)。现在他们也不再是“React.Children”废话了。这种方法确实应该更惯用 (2认同)

小智 5

我试图断言类型。你可以扔掉或者直接忽略。

interface TabbedViewProps {
  children?: React.ReactElement<ITabProps> | React.ReactElement<ITabProps>[]
}
Run Code Online (Sandbox Code Playgroud)

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

{React.Children.map(props.children, (tab) => {
  if(tab?.type != Tab) return;
  console.log(tab?.type == Tab);
  return tab;
})}
Run Code Online (Sandbox Code Playgroud)

  • `ab?.type != Tab` 中的“Tab”是什么? (4认同)

小智 -3

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

查看https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts查看 JSX.Element 界面的外观。