Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helper function for styles in TypeScript. #8819

Closed

Conversation

tvald
Copy link

@tvald tvald commented Oct 24, 2017

Adds a helper function style(cssProps: Partial<React.CSSProperties>): Partial<React.CSSProperties> to make TypeScript typing easier.

For a guide to using TypeScript with Material UI styles, see https://material-ui.com/guides/typescript/

With the current set of typings, the compiler is unable to infer appropriate typings for the arguments to withStyles():

const decorate = withStyles(theme => ({
  root: {
    overflow: 'hidden', // error
  },
}))

// Error:
// Types of property 'overflow' are incompatible.
// Type 'string' is not assignable to type '"hidden" | "initial" | "inherit" | "unset" | "auto" | "scroll" | "visible" | undefined'.

This can be fixed by adding the classname as a generic parameter, like so:

const decorate = withStyles<'root'>(theme => ({
  root: {
    overflow: 'hidden', // ok
  },
}))

However, this is cumbersome when the number of classnames becomes large, as in:

const decorate = withStyles<'root0' | 'root1' | 'root2' | 'root3' | 'root4' >(theme => ({
  root0: { overflow: 'hidden' },
  root1: { overflow: 'hidden' },
  root2: { overflow: 'hidden' },
  root3: { overflow: 'hidden' },
  root4: { overflow: 'hidden' },
}))

The addition of the style() helper assists with type inference, such that the above example becomes:

const decorate = withStyles(theme => ({
  root0: style({ overflow: 'hidden' }),
  root1: style({ overflow: 'hidden' }),
  root2: style({ overflow: 'hidden' }),
  root3: style({ overflow: 'hidden' }),
  root4: style({ overflow: 'hidden' }),
}))

This helper also enables the easy declaration of CSS properties outside of the context of withStyles(), eg, where a set of constant properties should be reused in multiple places:

const overflowHiddenStyle = style({ overflow: 'hidden' })

@sebald
Copy link
Member

sebald commented Oct 27, 2017

@oliviertassinari Is it fine by you having an identity function just for TypeScript? :)

I wonder if this may have some performance implications or if the compilers are smart enough and inline it 🤔

@eyn
Copy link
Contributor

eyn commented Oct 27, 2017

@sebald It feels quite fiddly to do this and I think makes the code less readable to do this, is there any time frame for if/when typescript will improve its type system so it doesn't need this sort work around?

@Pajn
Copy link
Contributor

Pajn commented Oct 27, 2017

Does the Typescript team know about the need to fix this? If not, maybe it's better to make them aware instead of silently working around the problem?

@sebald
Copy link
Member

sebald commented Oct 27, 2017

@eyn I am not part of the TypeScript team, so I can not tell you if or when it will be fixed 🙂

I also would not call it an improvement, rather it was a design decision AFAIK. Basically what happens is that TypeScript widens the types from 'hidden' to string. The later is obviously not compatible with CSSProperties['overflow'].

I had issues with widening several times (see microsoft/TypeScript#13948). The current workaround is to use type assertion. For example, at my company we write it like this instead:

type StyleClasses = 'root0' | 'root1' | 'root2' | 'root3' | 'root4';

const styles:StyleRulesCallback<StyleClasses> = theme => ({
  root0: { overflow: 'hidden' },
  root1: { overflow: 'hidden' },
  root2: { overflow: 'hidden' },
  root3: { overflow: 'hidden' },
  root4: { overflow: 'hidden' },
});

const Component = () => {}

export default withStyles(styles)(Component);

Which works fine and is very explicit, though more to write of course.


EDIT: I put down some more information about the problem with this in TS here.

@oliviertassinari
Copy link
Member

oliviertassinari commented Oct 27, 2017

Is it fine by you having an identity function just for TypeScript? :)

@sebald Hum. We would need a comment at least explaining why we export such function. I'm not using TypeScript, so it's not my call. The TypeScript experts decide 😁.

@eyn
Copy link
Contributor

eyn commented Oct 27, 2017

@sebald - thanks for the clarification. Is it not possible to use keyof to enumerate the keys?

@oliviertassinari - for this to work think we would also need to add style(....) to all style definitions in Mui not just export the style function or am I missing something?

@oliviertassinari
Copy link
Member

I'm closing this PR as have been inactive for 5 days. Feel free to open a new PR to push the discussion forward. I don't have much optioning on how the TypeScript should be handled. So, I'm gonna let people that know the topic decide. Thanks, everybody.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants