Material-ui: `createMuiTheme` return type doesn't take into account merged objects

Created on 5 Mar 2020  路  6Comments  路  Source: mui-org/material-ui

  • [x] The issue is present in the latest release.
  • [x] I have searched the issues of this repository and believe that this is not a duplicate.

Thank you so much for this project -- it is amazing!

Current Behavior 馃槸

I would like to add custom fields to the Theme object as described in the docs. However I struggle to get this to typecheck. The current Typescript definition for createMuiTheme is

export default function createMuiTheme(options?: ThemeOptions, ...args: object[]): Theme;

createMuiTheme returns a Theme object, rather than the type of deep merging options and args together.

Expected Behavior 馃

The return type of createMuiTheme would reflect that args was merged in. For example the following wouldn't fail (today it says Theme doesn't have field foo).

createMuiTheme({}, {foo: "bar"}).foo

Proposed Solution

One possible solution would be to change the return type of createMuiTheme to

export default function createMuiTheme<T>(
  options?: ThemeOptions,
  ...args: Array<Partial<T>>
): Theme & T;
discussion typescript

Most helpful comment

Customization is explained in our typescript guide. Does this work for you?

Changing the signature of createMuiTheme would be incomplete since it wouldn't affect the type you read from context.

All 6 comments

Customization is explained in our typescript guide. Does this work for you?

Changing the signature of createMuiTheme would be incomplete since it wouldn't affect the type you read from context.

@mcobzarenco it can works if you use this typed theme object directly, but not when it goes from the context

@eps1lon @testarossaaaaa Thank you so much for you guidance!

Customization is explained in our typescript guide. Does this work for you?

:+1: I'll try the solution described in the guide using declaration merging -- I'd argue it is preferable to express the relationship in the type system directly if possible.

Changing the signature of createMuiTheme would be incomplete since it wouldn't affect the type you read from context.

@mcobzarenco it can works if you use this typed theme object directly, but not when it goes from the context

ThemeProvider is already declared as generic in the type of Theme:

export default function ThemeProvider<T = DefaultTheme>(
  props: ThemeProviderProps<T>,
): React.ReactElement<ThemeProviderProps<T>>;

and similary the useTheme hook

export default function useTheme<T = DefaultTheme>(): T;

Such that something like the snippet below works ok with my proposed type signature for createMuiTheme:

const MY_THEME = createMuiTheme({}, {foo: "bar"});

function Component() {
  const foo = useTheme<typeof MY_THEME>().foo;
}

<ThemeProvider theme={MY_THEME}>
  <Component/>
</ThemeProvider>

I may be missing what you mean, I do apologize if that's the case, I'm new to Material UI -- is the issue useTheme<typeof MY_THEME>, i.e. explicitly specifying the return type?

I would recommend not using useTheme<MyTheme>(). We're probably removing this anyway since it's abusing generics. What you're actually doing is type casting so you should be explicit about it:

-useTheme<MyTheme>();
+useTheme() as MyTheme;

I'd argue it is preferable to express the relationship in the type system directly if possible.

Sure but you still need to tell all the context consumers what the type is. With module augmentation we provide the safest way to do that.

I'm going to close this issue since it's more suited for a discussion platform e.g. StackOverflow.

I would recommend not using useTheme(). We're probably removing this anyway since it's abusing generics. What you're actually doing is type casting so you should be explicit about it:

:+1:

FWIW when using declaration merging as suggested by the guide, I still have to cast to Theme when using the useTheme hook, i.e.

useTheme().foo

fails as useTheme() returns DefaultTheme, not Theme -- @eps1lon should the guide suggest declaration merging for DefaultTheme rather than Theme?

You're probably importing from @material-ui/styles not @material-ui/core/styles. Would be nice to include in the docs what is affect and what not :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zabojad picture zabojad  路  3Comments

revskill10 picture revskill10  路  3Comments

mb-copart picture mb-copart  路  3Comments

ryanflorence picture ryanflorence  路  3Comments

mattmiddlesworth picture mattmiddlesworth  路  3Comments