Reputation: 6491
I'm trying to extend Theme
with Material UI, but it's throwing an error, saying that I'm not extending it correctly, saying,
Property 'layout' is missing in type 'Palette' but required in type 'IPaletteOptions'.
Here is what I have:
// src/theme/theme.d.ts
import { Theme } from "@material-ui/core/styles";
import {
IPaletteOptions,
PaletteOptions
} from "@material-ui/core/styles/createPalette";
type TLayout = {
primary: string;
secondary: string;
};
declare module "@material-ui/core/styles/createPalette" {
export interface IPaletteOptions extends PaletteOptions {
layout: TLayout;
}
}
declare module "@material-ui/core/styles" {
export interface ITheme extends Theme {
palette: IPaletteOptions;
}
}
// src/theme/index.ts
import { createMuiTheme, ITheme } from "@material-ui/core/styles";
import { IPaletteOptions } from "@material-ui/core/styles/createPalette";
export const palette = {
layout: {
primary: "#ffffff",
secondary: "#e4e4e4"
}
} as IPaletteOptions;
export default createMuiTheme({ palette }) as ITheme;
// src/App.tsx
import React from "react";
import { ThemeProvider, ITheme } from "@material-ui/core";
import theme from "./theme";
import Component from "./Component";
export default function App() {
return (
<ThemeProvider<ITheme> theme={theme}>
<Component />
</ThemeProvider>
);
}
import { useTheme } from "@material-ui/core";
import React from "react";
export default React.memo(() => {
const theme = useTheme(); // <-- this should be of type `ITheme` automatically
// If I do useTheme<ITheme>(), it shows property 'layout', but
// I shouldn't have to do that, because of the type casting that
// I do in App.tsx, where I import and use 'theme' from src/theme/index.ts
return <div>the layout primary color is {theme.palette.layout.primary}</div>;
});
What am I doing wrong here? I would like to use ITheme
throughout my app, which would extend the basic Theme
in addition to what I add.
Upvotes: 8
Views: 14847
Reputation: 544
In Material v5 you can extend CommonColors
declare module "@mui/material/styles/createPalette" {
interface CommonColors {
layout: {
offBlack: string;
offWhite: string;
}
}
}
const colors = createTheme({
palette: {
common: {
layout: {
offBlack: "#14142B",
offWhite: "#FCFCFC",
},
},
},
});
// In a component
const useStyles = makeStyles((theme) => ({
target: {
backgroundColor: theme.palette.common.layout.offBlack
}
}));
Upvotes: 8
Reputation: 817
I got this working by using this:
declare module '@material-ui/styles' {
export interface DefaultTheme extends CustomTheme {}
}
Notice 'core' is not included in the module.
My full code:
import theme from './theme';
type CustomTheme = {
palette: typeof theme;
};
declare module '@material-ui/styles' {
export interface DefaultTheme extends CustomTheme {}
}
Upvotes: 0
Reputation: 17504
I'm thinking of it would be even handy to override things which uses your ITheme
like as following:
theme.d.ts
declare module "@material-ui/core/styles" {
export interface ITheme extends Theme {
palette: IPaletteOptions;
}
// Keep overriding these things in your app
// In this case returns `ITheme`
export function createMuiTheme(options?: ThemeOptions, ...args: object[]): ITheme;
// returns `ITheme`
export function useTheme<T = ITheme>(): T;
}
Then you no longer need to cast returned type anymore in your theme/index.ts
:
export default createMuiTheme({ palette });
In case of using useTheme
, it's supposed to return ITheme
as expected.
NOTE: I've also updated the codesandbox as well: https://codesandbox.io/s/charming-pasteur-s6h8v?file=/src/Component.tsx
Upvotes: 6