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

uinbv5nw  于 2023-01-02  发布在  React
关注(0)|答案(5)|浏览(203)

我试图利用TypeScript编译器中的recently added support for typing of children和@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[],尽管这是我使用的唯一子类型。
编辑:限制 * childprops * 的接口,而不是直接限制子组件的类型,这也是很好的,因为我所需要做的就是从子组件中拉出一些特定的props。

2g32fytz

2g32fytz1#

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

您应该尝试如下设置接口TabbedViewProps的子级

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

这里的想法不是告诉你的TabbedView有一个Tab的数组,而是告诉你的TabbedView他有一个element的数组,这个数组需要特定的 prop ,在你的例子中是TabProps
编辑(感谢马泰):

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

eivnm1vs2#

由于指针已经输出,因此将TabbedView.children声明为:

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

将消 debugging 误,但不会正确地对子元素进行类型检查,也就是说,您仍然可以将TabProps以外的子元素传递给TabbedView而不会得到任何错误,因此以下语句也是有效的:

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

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

    <NotTabButValidToo />
  </TabbedView>
);

相反,您可以声明一个prop,比如tabs: TabProps[],以传递创建这些Tab所需的prop(而不是它们的JSX),并在TabbedView中渲染它们:

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

...

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

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

    ...
  );
};
lyr7nygr

lyr7nygr3#

我试图Assert类型。你可以抛出或者忽略。

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

在组件本身中Map子组件并Assert或忽略

{React.Children.map(props.children, (tab) => {
  if(tab?.type != Tab) return;
  console.log(tab?.type == Tab);
  return tab;
})}
20jt8wwn

20jt8wwn4#

这些答案显示了一般的想法,但他们不允许你通过儿童喜欢:

<MyParentComponent>
  {condition && <Child1/>}
  {list.map((it) => <Child2 x={it}/>}
</MyParentComponent>

我从React(v16.14.21)代码库中type PropsWithChildren<P>的定义中得到了一些启发:

type PropsWithChildren<P> = P & { children?: ReactNode | undefined };

type ReactText = string | number;
type ReactChild = ReactElement | ReactText;

interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

并得出了一个适合我的用例的简化定义:

type TypedReactNode<T> = ReactElement<T> | Array<TypedReactNode<T>> | null | undefined;
type PropsWithTypedChildren<P, C> = P & { children?: TypedReactNode<C> | undefined };

最后,我可以这样定义我的组件:

type MyParentComponentProps = {
  whatever: string;
};

const MyParentComponent = (props: PropsWithTypedChildren<MyParentComponentProps, AllowedChildType>) => {
  // body
}
9o685dep

9o685dep5#

您在Tab呈现方法中返回的类型是JSX.Element。这就是问题的原因。TabbedView需要Tab类型的子级数组。我不确定您是否可以将某个组件指定为子级类型。它可以是string或JSX.Element。您可以显示Tab的定义文件吗?
查看https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts以了解JSX.Element接口的外观。

相关问题