javascript - Typescript Type to accept a React component type with subset of Props - Stack Overflow

There is a ponent Button with following props:ButtonProps = {variant: 'primary' | 'seco

There is a ponent 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 ponent called "ButtonGroup" ponent that accepts a Button instance as a prop but can only accept primary or secondary variant. How can I enforce that?

ButtonGroup ponent looks this:

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

Now, the props for ButtonGroup are as follwos:

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. But, with this current implementation, typescript doesn't plain if I provide a tertiary variant too.

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

There is a ponent 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 ponent called "ButtonGroup" ponent that accepts a Button instance as a prop but can only accept primary or secondary variant. How can I enforce that?

ButtonGroup ponent looks this:

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

Now, the props for ButtonGroup are as follwos:

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. But, with this current implementation, typescript doesn't plain if I provide a tertiary variant too.

 <ButtonGroup 
    primaryButton={<Button variant="tertiary">Submit</Button>} // TS SHOULD COMPLAIN BUT IT DOES NOT
   />
Share Improve this question asked Aug 29, 2022 at 17:28 AshimaAshima 4,8346 gold badges41 silver badges64 bronze badges 3
  • 1 AFAIK this is not possible: stackoverflow./q/42955400/4980215 – Ivan Shumilin Commented Sep 1, 2022 at 11:56
  • Would it be acceptable to just take in the primary button props and render the Button inside the ButtonGroup? – AlienWithPizza Commented Sep 2, 2022 at 1:42
  • This is a problem due to React, not Typescript. Check out this Playground where the type inference works fine and properly allows me to do what you're trying to do to above: typescriptlang/play?#code/… I'd also go with what @adrisons is suggesting, it makes more sense any way. – Forrest Commented Sep 6, 2022 at 6:29
Add a ment  | 

2 Answers 2

Reset to default 4 +25

In my opinion the cleanest solution would be to separate the implementation of each ponent 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

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

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

Hope this helps!

You can use PrimaryButton ponent instead of Button and it will highlight the variant other than primary or secondary.

Checkout the working CodeSandbox here.

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

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744977471a4604234.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信