繁体   English   中英

Typescript 中动态 React 组件的条件类型

[英]Conditional types for a dynamic React component in Typescript

尝试创建一个动态的 React 组件。 根据所选组件有条件地传递正确的 propType 的正确方法是什么。 这是我到目前为止我在这一行上收到的错误<SelectComponent {...props.props} />因为道具与组件不匹配:

export interface SidebarProps {
    type: keyof typeof components,
    props: AddEditJobsProps | AddEditCustomersProps
}

const components = {
  job: AddEditJobs,
  customer: AddEditCustomers
};

export default function Sidebar(props: SidebarProps) {
  const { open, toggle } = useToggle();
  const SelectComponent = components[props.type];

  return (
    <RightSidebar open={open} toggleDrawer={toggle}>
      <SelectComponent {...props.props} />
    </RightSidebar>
  );
}

编辑:道具添加any修复了错误,但是 Typescript 在类型检查期间将无法将所选类型与相应的道具匹配,这是我希望在这里完成的。

export interface SidebarProps {
    type: keyof typeof components,
    props: AddEditJobsProps | AddEditCustomersProps | any
}

如果您坚持以这种方式保持动态,那么您可能应该这样做。

正如我在评论中提到的,问题一是 TS 直到运行props.type知道props.type的值是什么,因此它无法有效地提前推断它应该是什么。 为了解决这个问题,你需要一个简单的旧条件来显式渲染正确的组件:

export const Sidebar: React.FC<SidebarProps> = props => {
  const { open, toggle } = useToggle();

  let inner: React.ReactNode;
  if (props.type === "job") {
    inner = <AddEditJobs {...props.props} />;
  } else if (props.type === "customer") {
    inner = <AddEditCustomers {...props.props} />;
  }

  return (
    <RightSidebar open={open} toggleDrawer={toggle}>
      {inner}
    </RightSidebar>
  );
};

请注意,条件语句基本上断言了该字段的值,这有助于 TS 推断形状的其余部分是什么并改进代码时类型检查。

问题二是您的SidebarProps界面过于宽松。

你有这个:

export interface SidebarProps {
    type: keyof typeof components,
    props: AddEditJobsProps | AddEditCustomersProps
}

这基本上是在告诉 Typescript“对象应该有一个匹配components一个键的type字段,以及一个是AddEditJobsPropsAddEditCustomersPropsprops字段”。 但它没有指定如果type等于"job"那么props字段必须匹配AddEditJobsProps 为此,您需要更明确地说明:

export type SidebarProps =
  | {
      type: "job";
      props: AddEditJobsProps;
    }
  | { type: "customer"; props: AddEditCustomersProps };

这使用联合类型来确保SidebarProps具有一种或另一种完整且有效的形状。

通过这些更改,不仅Sidebar中的 TS 错误消失了,而且当您在另一个组件中呈现它时,您将获得预期的正确 TS 检查。 如果type"job"props道具没有AddEditJobsProps的预期形状,你会得到一个错误。 在这个沙箱中亲自尝试一下:

https://codesandbox.io/s/youthful-fog-8lq41

我认为答案是这里的“动态组件”模式让事情变得困难。 人类和计算机都难以理解,导致代码难以阅读和维护,并且 Typescript 编译器无法准确检查。

这里使用的更好的模式是组件组合。 使<Sidebar>更通用,以便它不关心它呈现的子级,并且只处理打开/切换状态。

export default const Sidebar: React.FC = ({children}) => {
  const { open, toggle } = useToggle();

  return (
    <RightSidebar open={open} toggleDrawer={toggle}>
      {children}
    </RightSidebar>
  );
}

然后,当你想渲染一个侧边栏时,你只需给它你想要它包装的孩子:

<Sidebar>
  <AddEditJobs {...addEditJobsProps} />
</Sidebar>

// or

<Sidebar>
  <AddEditCustomers {...addEditCustomersProps} />
</Sidebar>

您将获得准确而严格的类型检查(因为 TS 将知道组件和道具的确切类型)并且您的代码结构将更具可读性和更易于遵循。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM