1

I'm attempting to access the current theme colors in a custom React component. The example I'm using is from Material UI http://www.material-ui.com/#/customization/themes

I've tried a number of variations, but cannot get Typescript to compile my component. The error I get is... error TS2345: Argument of type 'typeof MainHeader' is not assignable to parameter of type 'Component<{}, {}>'.

import * as React from "react";
import muiThemeable from 'material-ui/styles/muiThemeable';

interface Properties  {
  title: string
}

class MainHeader extends React.Component<Properties, any> {

render() {
  return (
    <div className='hero' >
      <div className='logo' />
      <div className="bar" />

      <h1>{this.props.title}</h1>
    </div>
  );
  }
}

export default muiThemeable()(MainHeader);

1 Answer 1

1

it seems to me the material-ui definition for muiThemeable function is not right ...

doing

export default muiThemeable()(MainHeader);

is the same as doing

export const themed = muiThemeable<MainHeader, {}, {}>()(MainHeader)

and doesn't compile because you omitted component property type and component property state

the compiler infers

<TComponent<P, S>,{}, {}>

which doesn't match the Function constraints

        export function muiThemeable<TComponent extends (React.Component<P, S>), P, S>()
            : (component: TComponent ) => TComponent;

... lets add the missing constrains

to satisfy the compiler , we should do

export default (props: Properties) => muiThemeable<MainHeader, Properties, any>()(new MainHeader(props));

but this is giving an instance to the function , when the F is expecting a class

if you later on do ...

// ...    
import Themeable from "./themeable";
let  props = { title: "hello!" };
ReactDOM.render(<Themeable {...props}/>, document.getElementById("main"));

it won't work

but if you change muiThemeable definition to:

 export function muiThemeable<TComponent extends (React.Component<P, S>), P, S>()
        : (component: Function ) => TComponent;

then you can use:

export default muiThemeable<MainHeader, Properties, any>()( MainHeader);

the tsc will generate errors

JSX element type 'Themeable' does not have any construct or call signatures

but it will transpile the right thing , and work

but that's not OK,

because:

  • it doesn't build
  • Function is NOT describing the right parameter type
  • neither TComponent describes what is returned,

it looks like its returning an instance when we need a class,... a type

finally:

this signature makes a bit more sense:

export function muiThemeable<TComponent extends (React.Component<P, S>), P, S>(): (component: new()=> TComponent ) => ( new() =>  TComponent);

but after looking at the source

export function muiThemeable<TComponent extends React.Component<P, S>, P extends MuiThemeProviderProps, S> (Component: new () => TComponent): React.StatelessComponent<P> 

And this could be a way to get away with re-writing or augmenting the definition.
Wrapping the function
... and perhaps using a decorator to reduce boilerplate and easier reading...

    import * as React from "react";
    import * as ReactDOM from "react-dom";
    import * as injectTapEventPlugin from "react-tap-event-plugin";

    // Needed for onTouchTap
    // http://stackoverflow.com/a/34015469/988941
    injectTapEventPlugin();


    import darkBaseTheme from "material-ui/styles/baseThemes/darkBaseTheme";
    import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
    import getMuiTheme from "material-ui/styles/getMuiTheme";
    import muiThemeable from "material-ui/styles/muiThemeable";

    import AppBar from "material-ui/AppBar";
    import MuiThemeProviderProps = __MaterialUI.Styles.MuiThemeProviderProps;

    function Themeable<TComponent extends React.Component<P, any>, P extends MuiThemeProviderProps> (Component: new () => TComponent): new() => TComponent {
        return muiThemeable<TComponent, P, any>()(Component as any) as any;
    }

    const themeable = <P, TFunction extends React.ComponentClass<P>>(target: TFunction): TFunction => {
        return Themeable(target as any) as any;
    };

    export interface MyBarProps extends __MaterialUI.AppBarProps, __MaterialUI.Styles.MuiThemeProviderProps {
        // ...
    }

    @themeable
    export class MyBar extends React.Component<MyBarProps, any> {

        constructor(props?: MyBarProps, context?: any) {
            super(props, context);
        }

        render() {
            if (!this.props.muiTheme) {
                throw new Error("muiTheme not Found");
            }
            return (
                <AppBar {...this.props} />
            );
        }
    }

    const darkTheme = getMuiTheme(darkBaseTheme);
    darkTheme.appBar.color = "red";

    const Main = () => (
        <MuiThemeProvider muiTheme={darkTheme}>
            <MyBar title="My AppBar" />
        </MuiThemeProvider>
    );

    ReactDOM.render(<Main></Main>,document.getElementById("root"));
Sign up to request clarification or add additional context in comments.

7 Comments

How exactly do you use this in TypeScript? Been trying to do the same thing as OP but can't seem to find the solution for this... Could you please post a full example on how one uses muiThemeable with TypeScript?
@RicardoAmaral please see if this helps, just uploaded it for your convenience :), buena suerte!
Was that link meant to point to that file exactly? Just cloned the whole project but couldn't find any reference to muiThemeable... Am I missing something?
you are right, there isn't, but if look at muiThemeable, you will see that just sets the props, from the context, that is pretty much what it does @inject from mobx. sorry about the link , I pasted the wrong url.
@RicardoAmaral, perhaps the edited answer helps a bit more... ?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.