-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
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
[typescript] Improve type definition for withStyles #8320
Conversation
Resolves #8267. |
@oliviertassinari I can adapt @ssalbdivad's testcase in #8310 to this. As an aside, I noticed that |
@pelotom 🙈 oops, I haven't added typescript for the CI correctly. We need it to pass! |
Codecov Report
@@ Coverage Diff @@
## v1-beta #8320 +/- ##
========================================
Coverage 99.42% 99.42%
========================================
Files 149 149
Lines 2256 2256
========================================
Hits 2243 2243
Misses 13 13 Continue to review full report at Codecov.
|
Ok, I updated the tests to reflect the new typings and also tested that this resolves #8267. |
test/typescript/styles.spec.tsx
Outdated
|
||
<StyledComponent text="I am styled!" />; | ||
|
||
// Also works with a plain object | ||
|
||
const stylesAsPojo: StyleRules = { | ||
const stylesAsPojo: StyleRules<'root'> = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it mean people will have to duplicate the styles keys, one in the definition and one in the object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, in fact you can now omit the type annotation completely here and get more type safety than you used to with the type annotation. You could also just annotate it as StyleRules
as before and get the same (reduced) type safety that you had before (i.e., forgetting the particular style names and treating them as string
s.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should rarely be any reason for users to have to reference StyleRules
explicitly in their code now.
test/typescript/styles.spec.tsx
Outdated
} | ||
|
||
const DecoratedComponent = withStyles(styles)(DecoratableComponent); | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is currently the least painful way in TypeScript that you can use a type-mutating decorator on a class.
Ok, I'm done tweaking this PR :) |
@pelotom The PR looks really great! Thanks a lot. Step by step you are making the typescript coverage high quality :). |
You’re welcome, glad to help out! |
Just came back from vacation, this is an awesome improvement 👍 Thx you sooo much! :) |
@oliviertassinari Can you add a BR notice on the next release for the TS typings? With this change it's no longer necessary to pass in a |
@sebald I'm not sure what I should be writing. Do you want to prepare the message for v1.0.0-beta.12? https://github.com/callemall/material-ui/blob/v1-beta/CHANGELOG.md |
I only have one caveat: I am not sure how people will react, if they can't use decorators anymore. This was requested a lot (even though the spec is constantly changing people seem to use it a lot with TS /shrug). |
@pelotom Could you explain what your reason was to move the props part to the returned HOC? :) Talking about this: - const StyledComponent = withStyles<
- StyledComponentProps,
- StyledComponentClassNames
- >(styles)(Component);
+ const StyledComponent = withStyles(styles)<StyledComponentProps>(
+ ({ classes, text }) => (
+ <div className={classes.root}>
+ {text}
+ </div>
+ )
+ ); Maybe we should support both options? Even though setting props beforehand may not be a good idea. But I see a lot of people writing their code like this (below code was removed by this PR): const Component: React.SFC<
StyledComponentProps & { classes: StyledComponentClassNames }
> = ({ classes, text }) =>
<div className={classes.root}>
{text}
</div>;
const StyledComponent = withStyles<
StyledComponentProps,
StyledComponentClassNames
>(styles)(Component); |
Due to TypeScript #4881 I don't know of a way to make
With this PR I sacrificed (3) in order to achieve the first 2, which I think are more important. |
The main reason is that the props variable const hoc = withStyles(styles) In principle you should now be able to apply
True, you could probably overload it to be more backwards compatible. It feels like an antipattern to annotate |
After playing with it some more, I think I've found an alternative typing that would allow using @withStyles(styles)
class DecoratedComponent extends StyledComponent<Props, 'root'> {
render() {
return (
<div className={this.props.classes!.root}> // <-- note the `!`
{this.props.text}
</div>
);
}
} This seems like a pretty good compromise, I'll open a PR and you can see what you think... |
Can you provide an example with an stateful component? I got it working with:
(I couldn't make it work with the class decorator) Is that how it is supposed to work? |
You’ll want to use |
See this comment for a good example. |
But if I use
This is the code:
And this is the error from the defaultProps
And this is the error from the wrapper
If I change defaultProps to be But... then what is the advantage of |
In general if your component is stateless, you should just use export default withStyles(styles)<Props>(props => /*...*/) because it doesn't require all the noise of mentioning |
@pelotom You are right, my second case doesn't make sense 🤦♂️. Now it works perfectly, many thanks. If someone comes here with a similar issue, this is how I solved it: import { StyledComponentProps } from 'material-ui';
type Props = {
prop1: string,
prop2?: string
} & StyledComponentProps;
@withStyles(styles)
class MyComponent extends Component<Props, State> {
static defaultProps: Partial<Props> = {
prop2: 'hello'
};
render() {
const class1 = this.props.classes!.class1; //Note the '!'
//...
}
} |
This improves the TypeScript typing for
withStyles
using mapped types, rendering it more type safe and requiring fewer explicit type arguments. For example, you can write:and it is fully type safe. The type of
props
is inferred to be bothNonStyleProps
and{ classes: { foo: string } }
.