11

I am trying to create a reusable component in reactjs with typescript. I am currently getting this error:

Type '{ children: string; type: string; }' is not assignable to type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.
  Type '{ children: string; type: string; }' is not assignable to type 'ButtonHTMLAttributes<HTMLButtonElement>'.
    Types of property 'type' are incompatible.
      Type 'string' is not assignable to type '"button" | "submit" | "reset" | undefined'.  TS2322

     7 | 
     8 | const Button = ({ text, ...otherProps }: IProps) => (
  >  9 |   <button {...otherProps}>
       |    ^
    10 |     { text }
    11 |   </button>
    12 | );

enter image description here

I am pretty new to typescript so I am not sure why I have this error. I think I am not assigning the correct types in my IProps interface in Button.tsx. I am not sure which type to assign

Button.tsx

import React from 'react';

interface IProps {
  text: string,
  type: string
}

const Button = ({ text, ...otherProps }: IProps) => (
  <button {...otherProps}>
    { text }
  </button>
);

export default Button;

How it is currently being used:

<Button type="submit">Sign up</Button>

4 Answers 4

12

Using type React.ComponentProps<'button'>) for Button component worked for me.

Sign up to request clarification or add additional context in comments.

1 Comment

For some reason this worked but React.HTMLProps<HTMLButtonElement> did not.
7

Typescript already has a definition of the types that are allowed in a button so you have to declare an enum which matches with the available types:

import React from 'react';

enum ButtonTypes {
  "button",
  "submit",
  "reset",
  undefined
} 

interface IProps {
  text: string,
  type: ButtonTypes
}

const Button = ({ text, ...otherProps }: IProps) => (
  <button {...otherProps}>
    { text }
  </button>
);

export default Button;

You can read more on enums here.

Comments

2

You can create your custom reusable component something like this, where you can pass your custom props and as well receive all the button props using ...rest. And now when you use this component you can just simply pass the onClick and even if you now don't specify this prop it will automatically get it, as it can infer all the default button elements props.

The cn is for className, it is a util function that helps us pass new classNames and overwrite the existing ones that makes it more reusable component.

import React from "react";
import { cn } from "@/lib/utils/twMerge";


interface ButtonProps extends 
React.ButtonHTMLAttributes<HTMLButtonElement> {
  className?: string;
  children: React.ReactNode;
 }

const Button = (props: ButtonProps) => {
  const { className, children, ...rest } = props;

  return (
    <button
      className={cn(
        "rounded-[20px] bg-accent-green min-h-[50px] text-[#000000] 
         text-[16px] font-normal font-poppins flex items-center 
        justify-center min-w-[160px] ",
        className
      )}
      {...rest}
    >
      {children}
    </button>
  );
};

export default Button;

Usage:

Here's how you can import/use and pass the type and onClick props without specifying these inside the custom Button Component.

import Button from "@/components/ui/Button";


<Button type="submit" onClick={()=>console.log("Clicked")}>
   Get started
</Button>

Here's how you can create the cn function. It is using two packages "tailwind-merge" and "clsx", so if you want to use this then make sure you install them as well and also create this function like this.

import { twMerge } from "tailwind-merge";
import { clsx, ClassValue } from "clsx";

export const cn = (...inputs: ClassValue[]) => {
  return twMerge(clsx(inputs));
};

Comments

-1

Typescript has got type definitions. This also applies to html elements such as buttons, inputs etc. If a reusable component is to be created, e.g "A button component" the type or interface needs to match what has been defined by typescript.

Quick fix in a few lines using an interface for a button element; The button types defined below are the same with what had be defined by typescript.

import React from 'react';
interface IProps {
  text: string,
  type: "button"|"submit"|"reset"|undefined
}

const Button = ({ text, ...otherProps }: IProps) => (
  <button {...otherProps}>
    { text }
</button>
);

export default Button;

This should fix the error message "Typescript with not assignable type error"

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.