Material-ui: [typescript] Types StyleRules and Theme incompatible.

Created on 14 Sep 2017  路  7Comments  路  Source: mui-org/material-ui

This issue applies to the v1-beta branch. The typescript type definitions of StyleRules and Theme are not compatible and cause a typescript type conflict.

As an example consider the following typical case:

import { withStyles, Theme, StyleRulesCallback } from 'material-ui/styles';

const styles: StyleRulesCallback = (theme: Theme) => ({
    root: theme.typography.button,
});

const Component = (props: any) => <p className={props.classes.root}>Test</p>;

export default withStyles(styles)(Component);

  • [x] I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior

The constant styles should correctly type check.

Current Behavior

The constant styles does not type check correctly. The problem is an incompatibility between the types StyleRules (defined in src/styles/withStyles.d.ts) and Theme (defined in src/styles/createMuiTheme.d.ts):

  • the object {root: theme.typography.button} is supposed to be of type StyleRule (because styles is declared to be StyleRulesCallback as expected by the call to withStyles)

    • the entry fontWeight in a StyleRule object is defined to be as defined in React.CSSProperties, i.e., "initial" | "inherit" | "unset" | "normal" | "bold" | "bolder" | "lighter" | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900

  • theme.typography.button is of type TypographyStyle, whose fontWeight property is of type number | string

Steps to Reproduce (for bugs)

  1. Just take the code above and typecheck with TypeScript.

Your Environment

| Tech | Version |
|--------------|---------|
| Material-UI | v1-beta |
| React | 15.6 |

typescript

Most helpful comment

@apieceofbart I guess you should try hinting style with StyleRules

const decorate = withStyles(() => {
  const style: StyleRules = {
    root: {
      fontWeight: 700,
    },
  };
  return style;
});

It should work

All 7 comments

A possible solution would be to reference the types in React.CSSProperties for the entries in TypographyStyle and FontStyle. This would conform to the type definition of StyleRule.

I provided a fix for this issue. See #8199.

@TorstenStueber Hi Torsten, I'd like to ask you after seeing your #8199 PR: is there any consideration why you didn't change fontWeightLight, fontWeightRegular, and fontWeightMedium as well? So it would be like

export interface FontStyle {
  fontFamily: React.CSSProperties['fontFamily'];
  fontSize: React.CSSProperties['fontSize'];
  fontWeightLight: React.CSSProperties['fontWeight'];
  fontWeightRegular: React.CSSProperties['fontWeight'];
  fontWeightMedium: React.CSSProperties['fontWeight'];
}

?

Because I encounter a similar issue when using theme.typography.fontWeightLight. But after changing fontWeightLight type to React.CSSProperties['fontWeight'] it typechecks.

I believe I have the same similar issue with fontWeight. Following code:

const decorate = withStyles(() => {
  const style = {
    root: {
      fontWeight: 700,
    },
  };
  return style;
});

give typescript error:

[ts]
Argument of type '() => { root: { fontWeight: number; }; }' is not assignable to parameter of type 'Record<"root", Partial> | StyleRulesCallback<"root">'.
Type '() => { root: { fontWeight: number; }; }' is not assignable to type 'StyleRulesCallback<"root">'.
Type '{ root: { fontWeight: number; }; }' is not assignable to type 'Record<"root", Partial>'.
Types of property 'root' are incompatible.
Type '{ fontWeight: number; }' is not assignable to type 'Partial'.
Types of property 'fontWeight' are incompatible.
Type 'number' is not assignable to type '700 | "initial" | "inherit" | "unset" | "normal" | "bold" | "bolder" | "lighter" | 100 | 200 | 30...'.

As I understand this has to do with the way Typescript interpretes type of fontWeight as number.
I know I can fix that by using as and specifing type e.g. fontWeight: 700 as React.CSSProperties["fontWeight"] but this kind of silly.
Isn't there a better way?

@apieceofbart I guess you should try hinting style with StyleRules

const decorate = withStyles(() => {
  const style: StyleRules = {
    root: {
      fontWeight: 700,
    },
  };
  return style;
});

It should work

@dewey92 thanks, I ended up doing it this way, this is by far the cleanest way.

@dewey92 thanks! that's very clean if you wrap it in a high order function like this:

// stylesFactory.ts

export default stylesFn => {
  const stylesResult = (theme, props) => {
    const styles = stylesFn(theme, props);
    return styles as StyleRules;
  };

  return stylesResult as StyleRulesCallback;
};

then you can use

const styles = stylesFactory(theme => ({}))
const StyledComponent = withStyles(styles)(Component);

without typing problems

Was this page helpful?
0 / 5 - 0 ratings

Related issues

iamzhouyi picture iamzhouyi  路  3Comments

rbozan picture rbozan  路  3Comments

mattmiddlesworth picture mattmiddlesworth  路  3Comments

ericraffin picture ericraffin  路  3Comments

revskill10 picture revskill10  路  3Comments