Skip to content Skip to sidebar Skip to footer

Extending Theme With Material-ui@next And Typescript

When creating my theme for material-ui I added two new palette options that give me a better range of lights and darks. I have extended the Theme type to indicate this import {Them

Solution 1:

The problem can be solved using module augmentation:

declaremodule'@material-ui/core' {
  interfaceTheme {
    colors: {
      success: {
        dark: string,
        light: string,
      }
    }
  }
}


Furthermore, you may declare the module in your App component as well as wrapping the children in a ThemeProvider:

import { createMuiTheme, ThemeProvider, colors, ThemeOptions } from'@material-ui/core';

declaremodule'@material-ui/core' {
  interfaceTheme {
    colors: {
      success: {
        dark: string,
        light: string,
      }
    }
  }
}

constApp = () => {
  const theme = createMuiTheme({
    colors: {
      success: {
        dark: colors.green[600],
        light: colors.green[300],
      },
    } asThemeOptions,
  });

  return (
    <ThemeProvidertheme={theme}><ahref="https://material-ui.com/customization/theming/">Theming</a></ThemeProvider>
  )

Solution 2:

The only answer I've come up with is to make my custom options optional like so

exportinterfaceExtendedPaletteextendsPalette {
    light?: Color,
    dark?: Color,
}

Then in my styles callback I have to check that those options exist, which is kind of a hassle, but I don't think there is any other workaround

conststyles = (theme : ExtendedTheme) => { 
    let light = theme.palette.light[100];
    if(light === undefined) light = theme.common.white;
    { root: {color: light }}
};

The reason for this is that the Theme object is passed to the callback when I use withStyles but the typings for this callback use the Theme type because they have no way of knowing about my ExtendedTheme type. The conflict comes in when ExtendedTheme must have options that Theme knows nothing about. By making those extra options optional Theme can still comply with ExtendedTheme. Basically an extended interface can be passed where its parent is expected, but its parent cannot be passed where the extended interface is expected, unless the extended interface is extended in a way that the Parent can still comply.

A simpler example is instructive.

export interface Foo {foo: string};
export interface Bar extends Foo {bar: string}

function getFoo(f : Foo) {console.log(f.foo)}
function getBar(b : Bar) {console.log(b.bar)} 
function getFooBar(fb: Bar) {console.log(fb.foo, fb.bar)}

const f : Foo = {foo: 'foo'}
const b : Bar = {foo: 'foo', bar: 'bar'}

getFoo(f) // foogetFoo(b) // foogetBar(f) // Error Incompatible TypegetBar(b) // bargetFooBar(f) // Error Incompatible TypegetFooBar(b) // foo bar

getFoo(b) works because Bar is guaranteed to have at least everything that Foo has. getBar(f) and getFooBar(f) both fail because the compiler sees that the type Foo does not have the key bar

By redefining Bar like so

exportinterfaceBarextendsFoo {bar? : string}

The compiler now knows that Foo matches the minimum qualifications for the Bar type, but you have to check for an implicit null. So this will work

getBar(f)

But the compiler will yell about implicit nulls, which is good, because f.bar is undefined. So you have to redefine your function like so

functiongetBar(b : Bar) {
    let bar = b.barif(bar === undefined) bar = b.foo;
    console.log(bar);
}

getBar(b) // bargetBar(f) // foo

Post a Comment for "Extending Theme With Material-ui@next And Typescript"