Search code examples
reactjstypescriptmaterial-uithemingmodule-augmentation

Difficulty implementing Typescript Augmentation for my MUI theme


I have a custom theme with some additional key/values added to the theme. This is causing TS errors when I go to use the design tokens in my app. I know I need to use module augmentation to fix it but honestly I am so confused as to where this goes, or how this would be done properly. I think the docs are really scarce on the topic, and other stackoverflows are just as brief with little context as to why or where they are getting the elements defined within their module augmented themes. The theme I built is below for reference. Thanks in advance, any and all help is appreciated.

The error I'm receiving is a variation of this below for each custom theme property:

Type '{ main: string; secondary: string; nav: { main: string; active: string; inactive: string; }; }' is not assignable to type 'Partial<TypeBackground>'.
  Object literal may only specify known properties, and 'main' does not exist in type 'Partial<TypeBackground>'

My current theme is below:

const muiTheme = createTheme({
  palette: {
    primary: { // orange
      main: '#ff5e3d',
      contrastText: '#e3e8f0'
    },
    secondary: { // black
      main: '#585858',
      contrastText: '#e3e8f0'
    },
    background: {
      //@ts-ignore
      main: '#FFFFFF', //white
      secondary: "#f1f5f9", //lightblue
      nav: {
        main: '#1e293b',
        active: '#0f172a',
        inactive: '#1e293b'
      }
    },
    info: undefined,
    text: {
      primary: "#11142d", 
      secondary: "#fff", 
      //@ts-ignore
      nav: "#64748b", 
      meta: "#94a3b8"
    },
    icons: {
      active: {
        primary: '#ff5e3d',
        secondary: '#ffffff',
      },
      inactive: {
        primary: '#94a3b8',
        secondary: '#475569',
      }
    },
    contrastThreshold: 1,
    tonalOffset: 0.2,
  },
  typography: {
    fontFamily: ['Gotham', 'Mulish', 'Helvetica', 'Arial', 'sans-serif'].join(","),
    h1: {
      fontFamily: "Gotham, Helvetica",
      fontWeight: 700,
      lineHeight: '24px',
      fontSize: '16px',
      textTransform: "uppercase",
    },
    h2: {   
      fontFamily: "Gotham, Helvetica",
      fontWeight: 400,
      lineHeight: '24px',
      fontSize: '16px',
    },
    h3: {
      fontFamily: "Mulish, Arial",
      fontWeight: 700,
      lineHeight: '24px',
      fontSize: '16px',
      textTransform: "uppercase",
    },
    subtitle1: {
      fontFamily: "Gotham, Helvetica",
      fontWeight: 400,
      lineHeight: '19.5px',
      fontSize: '13px',
    },
    body1: {
      fontFamily: "Mulish, Arial",
      fontWeight: 400,
      lineHeight: '21px',
      fontSize: '14px',
    },
    h4: undefined,
    h5: undefined,
    h6: undefined,
    body2: undefined,
    subtitle2: undefined,
    caption: undefined,
  },
  components: {
    MuiCssBaseline: {
      styleOverrides: `${[Gotham, Mulish]}`,
    },
    MuiButton: {
      styleOverrides: {
        root: ({ ownerState, theme }) => ({
          fontFamily: "Mulish, Arial",
          fontWeight: 700,
          lineHeight: '15.6px',
          fontSize: '13px',
          paddingY: 8,
          paddingX: 12,
          //@ts-ignore
          borderColor: theme.palette[ownerState.color].main,
          borderRadius: 22.5,
          borderWidth: 2,
          "&:hover": {
            borderWidth: 2
          }
        }),
      },
    },
    // change other base components here
  },
});

EDIT: I am also receiving a type error indicating that icon does not exist on type Palette. I'm using the useTheme hook in order to fetch the them object so I can get the theme values for an SVG that takes two color props. If active, the colors of the SVG change. This object is passed to the custom icon component and colors deconstructed, it currently works minus the TS error. The code for the active switch where the TS error is, is below:

const isActive = (
  active: boolean,
  theme: Theme,
): {
  background: string;
  iconPrimary: string;
  iconSecondary: string;
} => {
  return active
    ? {
        //@ts-ignore
        iconPrimary: theme.palette.icons.navIcon.active.primary,
        //@ts-ignore
        iconSecondary: theme.palette.icons.navIcon.active.secondary,
      }
    : {
        //@ts-ignore
        iconPrimary: theme.palette.icons.navIcon.inactive.primary,
        //@ts-ignore
        iconSecondary: theme.palette.icons.navIcon.inactive.secondary,
      };
};


Solution

  • Try the following. It should work!

    The code below augments the needed interfaces (More about augmentation in the official docs):

    declare module "@mui/material" {
      export interface TypeBackground {
        main: string;
        secondary: string;
        nav: {
          main: string;
          active: string;
          inactive: string;
        }
      }
    
      export interface PaletteOptions {
        icons: {
          active: {
            primary: string;
            secondary: string;
          },
          inactive: {
            primary: string;
            secondary: string;
          },
        }
      }
    
      export interface TypeText {
        nav: string;
        meta: string;
      }
    
      export interface Palette {
        icons: PaletteOptions['icons'];
      }
    }
    

    The snippets below fix the errors:

              borderColor: ownerState.color && ownerState.color !== 'inherit'
                ? theme.palette[ownerState.color].main
                : undefined,
    

    And:

    const isActive = (
      active: boolean,
      theme: Theme,
    ): {
      background?: string;
      iconPrimary: string;
      iconSecondary: string;
    } => {
      return active
        ? {
            iconPrimary: theme.palette.icons.active.primary,
            iconSecondary: theme.palette.icons.active.secondary,
          }
        : {
            iconPrimary: theme.palette.icons.inactive.primary,
            iconSecondary: theme.palette.icons.inactive.secondary,
          };
    };