0

I'm trying to create a React component with conditional props using a piped type between two interfaces. I have it set up like this:

interface BaseProps {
  "aria-label"?: string;
  className?: string;
  onClick?: () => void;
}

interface IconProps extends BaseProps {
  "aria-label": string;
  children: never;
  icon: React.ReactElement;
}

interface ButtonProps extends BaseProps {
  children: React.ReactElement;
}

type Props = ButtonProps | IconProps;

class Button extends React.Component<Props> {
  render(): React.ReactElement {
    const { children, icon, ...rest } = this.props;
    // ERROR: "Property 'icon' does not exist on type '(Readonly<ButtonProps> & Readonly<{ children?: ReactNode; }>) | (Readonly<IconProps> & Readonly<{ children?: ReactNode; }>)'."
  }
}

I can't seem to figure out how to achieve the following:

  1. When there is an "icon" prop, force "aria-label" and disallow children
  2. When there is not an "icon" prop, force children and allow optional "aria-label"
Griffen
  • 397
  • 1
  • 3
  • 22

1 Answers1

0

One way is to have a union between the two variants:

interface VariantWithIconProps {
  icon: React.ReactElement,
  "aria-label": string;
}

interface VariantNoIconProps {
  "aria-label"?: string;
  children: React.ReactNode,
}

type Props = VariantWithIconProps | VariantNoIconProps;

const iconProps: VariantWithIconProps = {
  icon: <svg/>,
  "aria-label": "a",
};
const props1: Props = iconProps;
const props2: VariantNoIconProps = iconProps; // typescript error

const noIconProps1: VariantNoIconProps = {
  children: <div/>
};
const props3: Props = noIconProps1;
const props4: VariantWithIconProps = noIconProps1;  // typescript error

const noIconProps2: VariantNoIconProps = {
  children: <div/>,
  'aria-label': "a"
};
const props5: Props = noIconProps2;
const props6: VariantWithIconProps = noIconProps2;  // typescript error

Unfortunately it looks like prohibiting attributes in an interface isn't easily done without a workaround.

brietsparks
  • 4,776
  • 8
  • 35
  • 69
  • Unfortunately, it looks like this only works for pure TS, but not with React. I ended up having to use the `UnionKeys`, `StrictUnionHelper`, and `StrictUnion` types from this answer: https://stackoverflow.com/questions/52771362/typescript-react-union-type-for-props-does-not-display-error-when-providing-exc – Griffen Aug 02 '20 at 04:50