键入 React 组件工厂函数

问题描述

给定类型

type EnumerableComponentFactory = <C,I>(config: {
  Container: React.ComponentType<C>;
  Item: React.ComponentType<I>;
}) => React.FC<{ items: I[] }>;

具有以下实现

const Enumerable: EnumerableComponentFactory =
  ({ Container,Item }) =>
  ({ items }) =>
    (
      <Container>
        {items.map((props,index) => (
          <Item key={index} {...props} />
        ))}
      </Container>
    );

和预期用途

const UnorderedList = Enumerable({
  Container: ({ children }) => <ul>{children}</ul>,Item: ({ title }: { title: string }) => <li>{title}</li>,});

<UnorderedList items=[{title: "Something"}] />

我观察到以下 TypeScript 错误

Type '{ children: Element[]; }' is not assignable to type 'C'.
  'C' Could be instantiated with an arbitrary type which Could be unrelated to '{ children: Element[]; }'.ts(2322)

这引出了我的问题:我需要设置什么类型的约束来解决这个错误

我尝试按如下方式更改类型:

type EnumerableComponentFactory = <C extends { children?: Element[] },I>(config: {
  Container: ComponentType<C>;
  Item: ComponentType<I>;
}) => (props: { items: I[] }) => ReturnType<FC<I>>;

但这会产生一个更加神秘的错误消息,为了简洁起见,我将省略它。


附言该函数本身实际上完全符合预期。只是编译器出了问题。

解决方法

是否需要保留 C 泛型参数?

import React,{ FC,ComponentType,PropsWithChildren } from "react";

type EnumerableComponentFactory = <I>(config: {
  // or Container: FC<{ children: JSX.Element[] }>;
  Container: FC<PropsWithChildren<object>>;
  Item: ComponentType<I>;
}) => FC<{ items: I[] }>;

const Enumerable: EnumerableComponentFactory =
  ({ Container,Item }) =>
  ({ items }) =>
    (
      <Container>
        {items.map((props,index) => (
          <Item key={index} {...props} />
        ))}
      </Container>
    );

const UnorderedList = Enumerable({
  Container: ({ children }) => <ul>{children}</ul>,Item: ({ title }: { title: string }) => <li>{title}</li>,});

const result = <UnorderedList items={[{ title: "Something" }]} />;

,

我能够更改您的代码以使其正常工作,同时还接受其他要传递给容器的道具:

type EnumerableComponentFactory = <C,I>(config: {
    Container: React.ComponentType<C & { children: React.ReactNode[] }>;
    Item: React.ComponentType<I>;
}) => React.ComponentType<C & { items: I[] }>;

const Enumerable: EnumerableComponentFactory = ({ Container,Item }) => (
    props
) => (
    <Container {...props}>
        {props.items.map((props,index) => (
            <Item key={index} {...props} />
        ))}
    </Container>
);

这允许例如这个:

const ContainerWithBorder: React.ComponentType<{ color: string }> = (props) => (
    <div style={{ border: `2px solid ${props.color}` }}>
        <ul>{props.children}</ul>
    </div>
);

const ComplexList = Enumerable({
    Container: ContainerWithBorder,Item: ({ title }: { title: string }) => <li>{title}</li>
});

<ComplexList items={[{ title: "Something" }]} color="red" />

ComplexList 组件带有 color 属性的输入/智能感知。

可以找到带有原始示例和 ComplexList 示例的游乐场 here