简体   繁体   English

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

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

There is a component Button with following props:有一个带有以下道具的组件按钮

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

Now, I want to create another component called "ButtonGroup" component that accepts a Button instance as a prop but can only accept primary or secondary variant.现在,我想创建另一个名为“ButtonGroup”组件的组件,该组件接受 Button 实例作为道具,但只能接受主要或次要变体。 How can I enforce that?我该如何执行?

ButtonGroup component looks this: ButtonGroup 组件如下所示:

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

Now, the props for ButtonGroup are as follwos:现在, 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>;
  };

I expect primaryButton to be a Button instance will all Button props but restricting variant to be either primary or secondary.我希望 primaryButton 是一个 Button 实例,它将所有 Button 道具,但将变体限制为主要或次要。 But, with this current implementation, typescript doesn't complain if I provide a tertiary variant too.但是,通过当前的实现,如果我也提供第三变体,typescript 不会抱怨。

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

In my opinion the cleanest solution would be to separate the implementation of each component to enforce its specific types.在我看来,最干净的解决方案是分离每个组件的实现以强制执行其特定类型。

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>
);

So, when you create a ButtonGroup, you should pass the specific PrimaryButton type, instead the generic one因此,当您创建一个 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
   />

Hope this helps!希望这可以帮助!

You can use PrimaryButton component instead of Button and it will highlight the variant other than primary or secondary .您可以使用PrimaryButton组件代替Button ,它将突出显示除primarysecondary以外的变体。

Checkout the working CodeSandbox here .此处检查工作的 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