Extending Theme With Material-ui@next And Typescript
Solution 1:
The problem can be solved using module augmentation:
declare module '@material-ui/core' {
interface Theme {
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';
declare module '@material-ui/core' {
interface Theme {
colors: {
success: {
dark: string,
light: string,
}
}
}
}
const App = () => {
const theme = createMuiTheme({
colors: {
success: {
dark: colors.green[600],
light: colors.green[300],
},
} as ThemeOptions,
});
return (
<ThemeProvider theme={theme}>
<a href="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
export interface ExtendedPalette extends Palette {
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
const styles = (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) // foo
getFoo(b) // foo
getBar(f) // Error Incompatible Type
getBar(b) // bar
getFooBar(f) // Error Incompatible Type
getFooBar(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
export interface Bar extends Foo {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
function getBar(b : Bar) {
let bar = b.bar
if(bar === undefined) bar = b.foo;
console.log(bar);
}
getBar(b) // bar
getBar(f) // foo
Post a Comment for "Extending Theme With Material-ui@next And Typescript"