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

[typescript] Improve type definition for withStyles #8320

Merged
merged 12 commits into from Sep 22, 2017
12 changes: 7 additions & 5 deletions src/index.d.ts
@@ -1,5 +1,7 @@
import * as React from 'react';

export type ClassNameMap<Names extends string = string> = Record<Names, string>;

/**
* Component exposed by `material-ui` are usually wrapped
* with the `withStyles` HOC and allow customization via
Expand All @@ -10,14 +12,14 @@ import * as React from 'react';
* - `style`
* - `innerRef`
*/
export interface StyledComponentProps<StyleClasses> {
export interface StyledComponentProps<Names extends string = string> {
className?: string;
classes?: StyleClasses;
classes?: ClassNameMap<Names>;
style?: Partial<React.CSSProperties>;
innerRef?: React.Ref<any>;
}
export class StyledComponent<P, C = Object> extends React.Component<
P & StyledComponentProps<C>
export class StyledComponent<P = {}, Names extends string = string> extends React.Component<
P & StyledComponentProps<Names>
> {}

export type Contrast = 'light' | 'dark' | 'brown';
Expand Down Expand Up @@ -94,7 +96,7 @@ export { CircularProgress, LinearProgress } from './Progress';
export { default as Radio, RadioGroup } from './Radio';
export { default as Select } from './Select';
export { default as Snackbar, SnackbarContent } from './Snackbar';
export { MuiThemeProvider, withStyles, withTheme, createMuiTheme } from './styles';
export { MuiThemeProvider, withStyles, WithStyles, withTheme, createMuiTheme } from './styles';

import * as colors from './colors';

Expand Down
2 changes: 1 addition & 1 deletion src/styles/index.d.ts
Expand Up @@ -3,6 +3,6 @@ export { default as createBreakpoints } from './createBreakpoints';
export { default as createMuiTheme, Theme } from './createMuiTheme';
export { default as createPalette } from './createPalette';
export { default as createTypography } from './createTypography';
export { default as withStyles } from './withStyles';
export { default as withStyles, WithStyles } from './withStyles';
export { StyleRules, StyleRulesCallback } from './withStyles';
export { default as withTheme } from './withTheme';
34 changes: 12 additions & 22 deletions src/styles/withStyles.d.ts
@@ -1,5 +1,5 @@
import * as React from 'react';
import { StyledComponentProps } from '..';
import { ClassNameMap, StyledComponentProps } from '..';
import { Theme } from './createMuiTheme';

/**
Expand All @@ -9,33 +9,23 @@ import { Theme } from './createMuiTheme';
* - the `keys` are the class (names) that will be created
* - the `values` are objects that represent CSS rules (`React.CSSProperties`).
*/
export interface StyleRules {
[displayName: string]: Partial<React.CSSProperties>;
}
export type StyleRules<Names extends string = string> = Record<Names, Partial<React.CSSProperties>>;

export type StyleRulesCallback = (theme: Theme) => StyleRules;
export type StyleRulesCallback<Names extends string = string> = (theme: Theme) => StyleRules<Names>;

export interface WithStylesOptions {
withTheme?: boolean;
name?: string;
}

declare function withStyles(
style: StyleRules | StyleRulesCallback,
options?: WithStylesOptions
): <
C extends React.ComponentType<P & { classes: ClassNames; theme?: Theme }>,
P = {},
ClassNames = {}
>(
component: C
) => C & React.ComponentClass<P & StyledComponentProps<ClassNames>>
export type WithStyles<P, Names extends string = string> = P & {
classes: ClassNameMap<Names>
theme?: Theme
};

declare function withStyles<P = {}, ClassNames = {}>(
style: StyleRules | StyleRulesCallback,
export default function withStyles<Names extends string>(
style: StyleRules<Names> | StyleRulesCallback<Names>,
options?: WithStylesOptions
): (
component: React.ComponentType<P & { classes: ClassNames; theme?: Theme }>
) => React.ComponentClass<P & StyledComponentProps<ClassNames>>

export default withStyles;
): <P>(
component: React.ComponentType<WithStyles<P, Names>>
) => React.ComponentType<P & StyledComponentProps<Names>>;
2 changes: 1 addition & 1 deletion test/typescript/components.spec.tsx
Expand Up @@ -632,7 +632,7 @@ const StepperTest = () =>
};

const TableTest = () => {
const styles: StyleRulesCallback = theme => ({
const styles: StyleRulesCallback<'paper'> = theme => ({
paper: {
width: '100%',
marginTop: theme.spacing.unit * 3,
Expand Down
56 changes: 28 additions & 28 deletions test/typescript/styles.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import {
withStyles,
StyleRules,
WithStyles,
createMuiTheme,
MuiThemeProvider,
Theme,
Expand All @@ -25,29 +25,25 @@ interface StyledComponentProps {
text: string;
}

const Component: React.SFC<
StyledComponentProps & { classes: StyledComponentClassNames }
> = ({ classes, text }) =>
<div className={classes.root}>
{text}
</div>;

const StyledComponent = withStyles<
StyledComponentProps,
StyledComponentClassNames
>(styles)(Component);
const StyledComponent = withStyles(styles)<StyledComponentProps>(
({ classes, text }) => (
<div className={classes.root}>
{text}
</div>
)
);

<StyledComponent text="I am styled!" />;

// Also works with a plain object

const stylesAsPojo: StyleRules = {
const stylesAsPojo = {
root: {
background: 'hotpink',
},
};

const AnotherStyledComponent = withStyles<{}, StyledComponentClassNames>({
const AnotherStyledComponent = withStyles({
root: { background: 'hotpink' },
})(({ classes }) => <div className={classes.root}>Stylish!</div>);

Expand Down Expand Up @@ -107,20 +103,24 @@ const AllTheStyles: React.SFC<AllTheProps> = ({ theme, classes }) =>
</div>;

const AllTheComposition = withTheme(
withStyles<{ theme: Theme }, StyledComponentClassNames>(styles)(AllTheStyles)
withStyles(styles)(AllTheStyles)
);

// As decorator
@withStyles(styles)
class DecoratedComponent extends React.Component<
StyledComponentProps & { classes: StyledComponentClassNames }
> {
render() {
const { classes, text } = this.props;
return (
<div className={classes.root}>
{text}
</div>
);
// Can't use withStyles effectively as a decorator in TypeScript
// due to https://github.com/Microsoft/TypeScript/issues/4881
// @withStyles(styles)
const DecoratedComponent = withStyles(styles)(
class extends React.Component<WithStyles<StyledComponentProps, 'root'>> {
render() {
const { classes, text } = this.props;
return (
<div className={classes.root}>
{text}
</div>
);
}
}
}
);

// no 'classes' property required at element creation time (#8267)
<DecoratedComponent text="foo" />