1

I would like to use custom variants in MUI v5. Why cannot use a custom variant as outlined in their documentation: https://mui.com/material-ui/customization/theme-components/#creating-new-component-variants

declare module "@mui/material/Button" {
  interface ButtonPropsVariantOverrides {
    icon: true;
    iconOnly: true;
  }
}


const muiButton = {
  MuiButton: {
    variants: [
      {
        props: { variant: "icon" },
        style: {
          background: palette.primary.main,
        },
      },
    ],
  },
};

createTheme({
  components: {
     ...muiButton 
  }
})

ts-error

TS2322: Type '{ MuiButton: { styleOverrides: { root: { fontStyle: string; fontSize: number; fontWeight: number; color: string; minWidth: string; borderRadius: number; "text-transform": string; boxShadow: string; "&.Mui-disabled": { ...; }; }; outlined: { ...; }; sizeSmall: { ...; }; sizeMedium: { ...; }; sizeLarge: { ...; }; }; v...' is not assignable to type 'Components<BaseTheme>'.   The types of 'MuiButton.variants' are incompatible between these types.     Type '({ props: { variant: string; size?: undefined; }; style: { background: string; color: string; "& .MuiSvgIcon-root": { height: number; }; "&.MuiButton-icon": { paddingRight: number; paddingLeft: number; }; ... 8 more ...; "&.Mui-disabled": { ...; }; }; } | { ...; } | { ...; } | { ...; })[]' is not assignable to type '{ props: Partial<ButtonProps<"button", {}>>; style: Interpolation<{ theme: Theme; }>; }[]'.       Type '{ props: { variant: string; size?: undefined; }; style: { background: string; color: string; "& .MuiSvgIcon-root": { height: number; }; "&.MuiButton-icon": { paddingRight: number; paddingLeft: number; }; ... 8 more ...; "&.Mui-disabled": { ...; }; }; } | { ...; } | { ...; } | { ...; }' is not assignable to type '{ props: Partial<ButtonProps<"button", {}>>; style: Interpolation<{ theme: Theme; }>; }'.         Type '{ props: { variant: string; size?: undefined; }; style: { background: string; color: string; "& .MuiSvgIcon-root": { height: number; }; "&.MuiButton-icon": { paddingRight: number; paddingLeft: number; }; ... 8 more ...; "&.Mui-disabled": { ...; }; }; }' is not assignable to type '{ props: Partial<ButtonProps<"button", {}>>; style: Interpolation<{ theme: Theme; }>; }'.           The types of 'props.variant' are incompatible between these types.            

 Type 'string' is not assignable to type '"icon" | "iconOnly" | "text" | "outlined" | "contained" | undefined'.

enter image description here

Jamie Hutber
  • 26,790
  • 46
  • 179
  • 291
  • Does this answer your question? [Typescript Type 'string' is not assignable to type](https://stackoverflow.com/questions/37978528/typescript-type-string-is-not-assignable-to-type) – Tobias S. Jun 24 '22 at 14:50
  • Sadly not, as this is a typing issue caused by a third party tool. MUI, rather than any types I can create myself – Jamie Hutber Jun 24 '22 at 14:55
  • If you look at the first answer, you will see that you have to declare your variable with `as const`. This will solve your problem. – Tobias S. Jun 24 '22 at 14:57
  • That does not work I believe. It is already a const. `const muiButton = {} as const`? I think there is a small chance you are misunderstanding my question around MUIs declaring of variants? – Jamie Hutber Jun 24 '22 at 15:00
  • 1
    Yes, write `as const` **behind** the object literal or write `as const` behind `"icon"` – Tobias S. Jun 24 '22 at 15:03
  • Ye, this is what I have done and no luck. Thank you for trying to help. But just to reiterate. I have not declared these types. The answer you have linked to seems to be when you have declared your own types. – Jamie Hutber Jun 24 '22 at 15:07
  • Does the error message change when using `as const` behind `"icon"` ? – Tobias S. Jun 24 '22 at 15:15
  • Also can you tell me your version of `@mui/material`? I am getting a slightly different error message here: `Type 'string' is not assignable to type '"text" | "outlined" | "contained" | undefined'` – Tobias S. Jun 24 '22 at 15:17
  • Thats the same error as I'm getting, I removed the `iconOnly` just to simply the example. :) But ye, you are getting teh same error – Jamie Hutber Jun 24 '22 at 15:19
  • In your error message `"icon"` is part of the union. `"icon"` is not an option for me locally. But when I use `"text"` it works. – Tobias S. Jun 24 '22 at 15:20
  • That is because `text` is part of the typing as defined here: https://mui.com/material-ui/api/button/ So `'contained' | 'outlined' | 'text' | string` will all work – Jamie Hutber Jun 24 '22 at 15:21
  • I have seen that you put a bounty on this question. Just to repeat: putting `as const` behind `"icon"` still fixes the problem: https://tsplay.dev/w8EVEN. If this does not fix your issue, please provide a stand-alone playground which demonstrates the error you are getting. – Tobias S. Jun 27 '22 at 08:43

3 Answers3

9

You have to narrow props.variant down to an acceptable type, string is too broad, MUI can't work with string here.

Do this:

props: { variant: "icon" as const }

… or this:

props: { variant: "icon" } as const

… or this:

import { ButtonProps } from "@mui/material"
props: { variant: "icon" } as ButtonProps
Parzh from Ukraine
  • 7,999
  • 3
  • 34
  • 65
1

Since you are already using createTheme, and it is perfectly typed, you can just put the definition of MuiButton directly inside the call, without using muiButton object:

createTheme({
  components: {
    MuiButton: {
      variants: [
        {
          props: { variant: "icon" },
          style: {
            background: palette.primary.main,
          },
        },
      ],
    },
  }
})

Try it.

This makes props.variant implicitly typed, and produces no compiler errors.

Parzh from Ukraine
  • 7,999
  • 3
  • 34
  • 65
0

Actually it's simple problem to solve it there is two ways whether don't use a const muibutton and destructur it at all, just work directly in Your createTheme. If you really want to use a const out side the createTheme then you have to create a type or interface to muibutton where variant not type 'string' it's '"icon" | "iconOnly" | "text" | "outlined" | "contained" | undefined' you have to type it because in you `const muibutton ...``variant it's type string.

Edit: Also i' have seen the discussion about using as const yes you can do that too but you have to do something like this as an example

props: { variant: "icon" , color: "primary",size:'small' } as const,

If it's not clear tell me to make it clear.

ZomitaKa
  • 475
  • 2
  • 7