繁体   English   中英

Typescript 类型以接受带有 Props 子集的 React 组件类型

[英]Typescript Type to accept a React component type with subset of Props

有一个带有以下道具的组件按钮

ButtonProps = {
  variant: 'primary' | 'secondary' | 'tertiary';
  label: string;
  // a few more props like onChange, size etc.
}

现在,我想创建另一个名为“ButtonGroup”组件的组件,该组件接受 Button 实例作为道具,但只能接受主要或次要变体。 我该如何执行?

ButtonGroup 组件如下所示:

 <ButtonGroup 
    primaryButton={<Button variant="primary">Submit</Button>}
    otherButton={<Button variant="secondary">Cancel</Button>}
   />

现在, ButtonGroup的 props 如下所示:

type PrimaryButtonProps = Omit<ButtonProps, 'variant'> & {
  variant: 'primary' | 'secondary';
};

type ButtonGroupProps = BaseComponentProps<'div'> & {
    size?: 'small' | 'medium';
    primaryButton: React.ReactElement<PrimaryButtonProps, typeof Button>;
    otherButton?: React.ReactElement<OtherButtonProps, typeof Button>;
  };

我希望 primaryButton 是一个 Button 实例,它将所有 Button 道具,但将变体限制为主要或次要。 但是,通过当前的实现,如果我也提供第三变体,typescript 不会抱怨。

 <ButtonGroup 
    primaryButton={<Button variant="tertiary">Submit</Button>} // TS SHOULD COMPLAIN BUT IT DOES NOT
   />

在我看来,最干净的解决方案是分离每个组件的实现以强制执行其特定类型。

interface ButtonProps {
  variant: "primary" | "secondary" | "tertiary";
  children?: React.ReactNode;
}

const Button = ({ variant, children }: ButtonProps): React.ReactElement => (
  <button>{children}</button> // apply styles based on the variant
);

interface PrimaryButtonProps {
  label: string;
  variant: "primary" | "secondary";
}

const PrimaryButton = ({ label, variant }: PrimaryButtonProps) => (
  <Button variant={{ variant }}>{{ label }}</Button>
);

因此,当您创建一个 ButtonGroup 时,您应该传递特定的 PrimaryButton 类型,而不是通用的

type ButtonGroupProps = BaseComponentProps<'div'> & {
    size?: 'small' | 'medium';
    primaryButton: React.ReactElement<PrimaryButtonProps, typeof PrimaryButton>;
    // ...
  };

<ButtonGroup 
    primaryButton={<PrimaryButton variant="tertiary">Submit</PrimaryButton>} // TS should complain here
   />

希望这可以帮助!

您可以使用PrimaryButton组件代替Button ,它将突出显示除primarysecondary以外的变体。

此处检查工作的 CodeSandbox。

interface ButtonProps {
  variant: "primary" | "secondary" | "tertiary";
  label: string;
}

function Button({ variant, label }: ButtonProps) {
  return (
    <button style={{ color: variant === "primary" ? "blue" : "red" }}>
      {label}
    </button>
  );
}

type PrimaryButtonProps = Omit<ButtonProps, "variant"> & {
  variant: "primary" | "secondary";
};

interface ButtonGroupProps {
  primaryButton: React.ReactElement<PrimaryButtonProps>;
}

function ButtonGroup({ primaryButton }: ButtonGroupProps) {
  return <div>{primaryButton}</div>;
}

// See below type assertion. It's a hack but it works :)
const PrimaryButton = Button as (props: PrimaryButtonProps) => JSX.Element;

export default function App() {
  return (
    <div className="App">
      <ButtonGroup
        primaryButton={<PrimaryButton variant="tertiary" label="Primary" />}
      />
    </div>
  );
}

暂无
暂无

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

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