16

I'm writing a React app using TypeScript. I use material-ui for my components. I'm writing a custom wrapper for material-ui's Button component. It looks like this:

import MUIButton, { ButtonProps } from "@material-ui/core/Button";
import withStyles, { WithStyles } from "@material-ui/core/styles/withStyles";
import classNames from "classnames";
import React, { PureComponent } from "react";
import styles from "./styles";

export interface OwnProps {
  color: "primary" | "danger" | "warning" | "transparent";
  size: "sm" | "lg";
}

export interface Props
  extends WithStyles<typeof styles>,
    OwnProps,
    ButtonProps {}

export class Button extends PureComponent<Props> {
  render() {
    const { color, size, classes, children, ...rest } = this.props;
    const btnClasses = classNames({
      [classes.button]: true,
      [classes[size]]: size,
      [classes[color]]: color
    });
    return (
      <MUIButton {...rest} className={btnClasses}>
        {children}
      </MUIButton>
    );
  }
}

export default withStyles(styles)(Button);

The problem is that here that the definition of Props throw the error message:

Named property 'color' of types 'OwnProps' and 'ButtonProps' are not identical.
[ts]
Interface 'Props' cannot simultaneously extend types 'OwnProps' and 'ButtonProps'.
  Named property 'size' of types 'OwnProps' and 'ButtonProps' are not identical.
Named property 'color' of types 'OwnProps' and 'ButtonProps' are not identical.
[ts]
Interface 'Props' cannot simultaneously extend types 'OwnProps' and 'ButtonProps'.
  Named property 'size' of types 'OwnProps' and 'ButtonProps' are not identical.

This error goes away if I instead write:

export class Button extends PureComponent<Props & ButtonProps> {

But then when using the Button the props color and size throw the error:

The expected type comes from property 'color' which is declared here on type 'IntrinsicAttributes & Pick<Props & ButtonProps, ...

How can I correctly tell the component that it has the the Props I defined (OwnProps) as well as the props that come from the Button as usual?

2
  • Please include the code that has the error and the full text of the error (or at least the main part, "type X is not assignable to type Y") Commented Nov 3, 2018 at 21:06
  • @jcalz I eddited the question 😊Thank you for your help! Commented Nov 4, 2018 at 8:42

1 Answer 1

23

Use TypeScript's Omit type to exclude specific properties from another type:

Constructs a type by picking all properties from Type and then removing Keys (string literal or union of string literals).

import { ButtonProps } from "@material-ui/core/Button";

export type OwnProps = Omit<ButtonProps, "color" | "size"> & {
  color: "primary" | "danger" | "warning" | "transparent";
  size: "sm" | "lg";
}

class MyButton extends React.Component<OwnProps> {
}
Sign up to request clarification or add additional context in comments.

6 Comments

I hope it helps
This works as far as the OwnProps warning goes, but now for export interface Props extends WithStyles<typeof styles>, OwnProps {} I get the error: <too long see second comment> and 'OwnProps' are not identical.` Similar to the second one I described in my question. Do you have any idea how to solve this?
Interface 'Props' cannot simultaneously extend types '{ classes: Record<"primary" | "danger" | "warning" | "transparent" | "sm" | "lg" | "button", string>; innerRef?: string | ((instance: any) => any) | RefObject<any> | undefined; }' and 'OwnProps'. Named property 'classes' of types '{ classes: Record<"primary" | "danger" | "warning" | "transparent" | "sm" | "lg" | "button", string>; innerRef?: string | ((instance: any) => any) | RefObject<any> | undefined; }
Yes, this happens because mui button expects its own classes, so you could exclude "classes" in Omit as well, it should work then
This answer doesn't actually explain how to solve the problem. The solution is very specific to this question, which is ok, but adding an explanation will allow it to provide value to others with similar but unique circumstances surrounding their question.
|

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.