Typescript Module augmentation is not working: Property 'main' does not exist on type 'PaletteColorOptions'

Viewed 2928

I have been working on Material-UI and trying to use a color system throughout the palette. There seems to be some issue while compilation although it works perfectly in run time. Can someone help me resolve following error:

Error:

Property 'main' does not exist on type 'PaletteColorOptions'.
Property 'main' does not exist on type 'Partial'.(2339)

Here is stackblitz as well: https://stackblitz.com/edit/react-up6bjl-hx1bbh?file=demo.tsx

Code:

import * as React from 'react';
import {
  createTheme,
  Theme,
  ThemeProvider,
  PaletteOptions
} from '@material-ui/core/styles';

import Button from '@material-ui/core/Button';

declare module '@material-ui/core/styles' {
  interface SimplePaletteColorOptions {
    lighter?: string;
    darker?: string;
  }

  interface PaletteColor {
    lighter?: string;
    darker?: string;
  }
}

const Default = () : PaletteOptions => {

  return {
    primary: {
      lighter: '#ddd',
      light: '#ddd',
      main: '#ddd',
      dark: '#ddd',
      darker: '#ddd'
    },
  };
};

export default function CustomColor() {
  const defaultColors = Default();
  
  const palette: PaletteOptions = {
    ...defaultColors,
    divider: defaultColors.primary?.main, // error in compile. Cannot find 'main'
  };

  const theme: Theme = createTheme({
    palette
  });

  console.log(theme.palette.primary.light);

  return (
    <ThemeProvider theme={theme}>
      <Button color="primary" variant="contained">
        neutral
      </Button>
    </ThemeProvider>
  );
}
1 Answers

The TypeScript error is unrelated to your module augmentation. The issue is just that defaultColors is of type PaletteOptions. PaletteOptions defines primary to be of type PaletteColorOptions.

Here's the definition of PaletteColorOptions and the types it is built from:

export type PaletteColorOptions = SimplePaletteColorOptions | ColorPartial;

export interface SimplePaletteColorOptions {
  light?: string;
  main: string;
  dark?: string;
  contrastText?: string;
}

export type ColorPartial = Partial<Color>;

export interface Color {
  50: string;
  100: string;
  200: string;
  300: string;
  400: string;
  500: string;
  600: string;
  700: string;
  800: string;
  900: string;
  A100: string;
  A200: string;
  A400: string;
  A700: string;
}

So the TypeScript compiler knows that defaultColors.primary is either SimplePaletteColorOptions or ColorPartial, but it doesn't know which. You are then referencing defaultColors.primary.main, but that isn't guaranteed to be present unless the type of defaultColors.primary is SimplePaletteColorOptions.

You can fix this by using a more specific return type for the Default function that lets TypeScript know that the type of primary is SimplePaletteColorOptions:

interface DefaultPaletteOptions extends PaletteOptions {
  primary?: SimplePaletteColorOptions;
}
const Default = (): DefaultPaletteOptions => {
  return {
    primary: {
      lighter: "#ddd",
      light: "#ddd",
      main: "#ddd",
      dark: "#ddd",
      darker: "#ddd"
    }
  };
};

Edit TypeScript PaletteOptions

Related answers:

Related