Overload error from extending custom props with React.ComponentProps

I want to preserve the Props from the base JSX button element, so whenever I use my custom styled-component it acts like a normal button with styles and some custom props on top of it.

here’s my implementation:

// button-base.styles.ts
import styled from 'styled-components';

export interface ButtonBaseProps extends React.ComponentProps<'button'> {}

const ButtonBase = styled.button<ButtonBaseProps>`
  cursor: pointer;
  padding: 0;
  border: none;
  outline: none;
  background: transparent;
`;

export const S = { ButtonBase };

// button-base.component.tsx
import React from 'react';

import { S, ButtonBaseProps } from './button-base.styles';

export const ButtonBase: React.FC<ButtonBaseProps> = ({ children, ...props }) => (
  <S.ButtonBase {...props}>{children}</S.ButtonBase>
);
// error

No overload matches this call.
  Overload 1 of 2, '(props: Pick<Pick<Pick<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "form" | ... 265 more ... | "css"> & { ...; } & ButtonBaseProps, "form" | ... 266 more ... | "css"> & Partial<...>, "form" | ... 266 more ... | "css"> & { ...; } & { ...; }): ReactElement<...>', gave the following error.
    Type '{ children: ReactNode; ref?: string | ((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | null | undefined; ... 265 more ...; css?: InterpolationWithTheme<...>; }' is not assignable to type 'Pick<Pick<Pick<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "form" | ... 265 more ... | "css"> & { ...; } & ButtonBaseProps, "form" | ... 266 more ... | "css"> & Partial<...>, "form" | ... 266 more ... | "css">'.
      Types of property 'ref' are incompatible.
        Type 'string | ((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | null | undefined' is not assignable to type '((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | (((instance: HTMLButtonElement | null) => void) & string) | ... 4 more ... | undefined'.
          Type 'string' is not assignable to type '((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | (((instance: HTMLButtonElement | null) => void) & string) | ... 4 more ... | undefined'.
  Overload 2 of 2, '(props: StyledComponentPropsWithAs<"button", DefaultTheme, ButtonBaseProps, never>): ReactElement<StyledComponentPropsWithAs<"button", DefaultTheme, ButtonBaseProps, never>, string | ... 1 more ... | (new (props: any) => Component<...>)>', gave the following error.
    Type '{ children: ReactNode; ref?: string | ((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | null | undefined; ... 265 more ...; css?: InterpolationWithTheme<...>; }' is not assignable to type 'Pick<Pick<Pick<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "form" | ... 265 more ... | "css"> & { ...; } & ButtonBaseProps, "form" | ... 266 more ... | "css"> & Partial<...>, "form" | ... 266 more ... | "css">'.
      Types of property 'ref' are incompatible.
        Type 'string | ((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | null | undefined' is not assignable to type '((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | (((instance: HTMLButtonElement | null) => void) & string) | ... 4 more ... | undefined'.
          Type 'string' is not assignable to type '((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | (((instance: HTMLButtonElement | null) => void) & string) | ... 4 more ... | undefined'.ts(2769)

Reproduction

https://codesandbox.io/s/quirky-voice-gm2od

1 possible answer(s) on “Overload error from extending custom props with React.ComponentProps

  1. thanks @kitten, I ended up with this solution to mimic the base jsx element (‘button’) behavior

    // button.styles.ts
    import styled from 'styled-components';
    
    export interface ButtonBaseProps extends React.ComponentPropsWithoutRef<'button'> {}
    
    const ButtonBase = styled.button<ButtonBaseProps>`
      cursor: pointer;
      padding: 0;
      border: none;
      outline: none;
      background: transparent;
    `;
    
    export const S = { ButtonBase };
    
    
    // button-base.component.tsx
    import React, { forwardRef } from 'react';
    
    import { S, ButtonBaseProps } from './button-base.styles';
    
    export const ButtonBase = forwardRef(
      (props: ButtonBaseProps, ref: React.Ref<HTMLButtonElement>) => (
        <S.ButtonBase ref={ref} {...props} />
      ),
    );