-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
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
[docs] TypeScript Guide #8598
Comments
@oliviertassinari @pelotom ping! 😎 Any suggestions? |
This sounds great. I can't think of anything else. |
It sounds good to me, although I'm pretty busy this week and may not be able to get to it in the near term... |
I would also appreciate it if we could also have a typescript version of the example code. |
I attempted to use withStlyes today with typescript, and failed. A basic example would be appreciated. The following fails:
Also when |
stateless functional componentimport * as React from 'react';
import { StyleRulesCallback, WithStyles, withStyles } from 'material-ui/styles';
import Badge from 'material-ui/Badge';
import Icon from 'material-ui/Icon';
type C = 'badge';
interface P { }
const styles: StyleRulesCallback<C> = (theme) => ({
badge: {
margin: `0 ${theme.spacing.unit * 2}px`,
},
});
function BadgeDemo({ classes }: P & WithStyles<C>) {
return (
<div>
<Badge className={classes.badge} badgeContent={4} color="primary">
<Icon>mail</Icon>
</Badge>
<Badge className={classes.badge} badgeContent={10} color="accent">
<Icon>folder</Icon>
</Badge>
</div>
);
}
export default withStyles(styles)(BadgeDemo); anonymous stateless functional componentimport * as React from 'react';
import { withStyles } from 'material-ui/styles';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import Typography from 'material-ui/Typography';
import IconButton from 'material-ui/IconButton';
import Icon from 'material-ui/Icon';
import Button from 'material-ui/Button/Button';
const ButtonAppBar = withStyles((theme) => ({
root: {
marginTop: theme.spacing.unit * 3,
width: '100%',
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
loginButton: {
marginLeft: 'auto',
},
}))(({ classes }) => (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton className={classes.menuButton} color="contrast" aria-label="menu">
<Icon>menu</Icon>
</IconButton>
<Typography type="title" color="inherit">
Title
</Typography>
<Button className={classes.loginButton} color="contrast">
Login
</Button>
</Toolbar>
</AppBar>
</div>
));
export default ButtonAppBar; class componentimport * as React from 'react';
import { withStyles, WithStyles } from 'material-ui/styles';
import List, {
ListItem,
ListItemText
} from 'material-ui/List';
import Menu, { MenuItem } from 'material-ui/Menu';
import { StyledComponent } from 'material-ui';
type C = 'root';
interface P {
options?: string[];
}
interface S {
open: boolean;
anchorEl?: HTMLButtonElement;
selectedIndex: number;
}
@withStyles<C>((theme) => ({
root: {
maxWidth: 360,
width: '100%',
backgroundColor: theme.palette.background.paper,
},
}))
class SelectedMenu extends React.Component<P & WithStyles<C>, S> {
static defaultProps: Partial<P & WithStyles<C>> = {
options: [
'Show all notification content',
'Hide sensitive notification content',
'Hide all notification content',
],
};
constructor(...args) {
super(...args);
this.state = {
open: false,
selectedIndex: 0,
};
}
handleListItemClick = (event: React.MouseEvent<{}>) => {
this.setState({
open: true,
anchorEl: event.currentTarget as HTMLButtonElement,
});
}
handleRequestClose = () => {
this.setState({ open: false });
}
createHandleMenuItemClick = (selectedIndex: number): React.MouseEventHandler<{}> => () => {
this.setState({
selectedIndex,
open: false,
});
}
render() {
const id = 'lock-menu';
const { classes, options } = this.props;
const { selectedIndex, open, anchorEl } = this.state;
return (
<div className={classes.root}>
<List>
<ListItem
button
aria-haspopup={true}
aria-controls={id}
aria-label="When device is locked"
onClick={this.handleListItemClick}
>
<ListItemText
primary="When device is locked"
secondary={options![selectedIndex]}
/>
</ListItem>
</List>
<Menu
id={id}
open={open}
anchorEl={anchorEl}
onRequestClose={this.handleRequestClose}
>
{options!.map((option, i) => (
<MenuItem
key={option}
selected={i === selectedIndex}
onClick={this.createHandleMenuItemClick(i)}
>
{option}
</MenuItem>
))}
</Menu>
</div>
);
}
}
export default SelectedMenu as StyledComponent<P, C>; |
TypeScript class decorators are no longer supported. Instead I would write this as const SelectedMenu = withStyles(theme => ({
root: {
maxWidth: 360,
width: '100%',
backgroundColor: theme.palette.background.paper,
},
}))<P>(class extends React.Component<P & WithStyles<C>, S> { |
@pelotom // v1.0.0-beta.15
interface StyledComponentDecorator<ClassKey extends string = string> {
// StatelessComponent
<P>(
component: React.StatelessComponent<P & WithStyles<ClassKey>>
): StyledComponent<P, ClassKey>;
// ComponentClass
<P, C extends React.ComponentClass<P & StyledComponentProps<ClassKey>>>(
component: C
): C;
}
// v1.0.0-beta.16
// interface StyledComponentDecorator<ClassKey extends string = string> {
// <P>(
// component: React.ComponentType<P & WithStyles<ClassKey>>
// ): StyledComponent<P, ClassKey>;
// }
export default function withStyles<ClassKey extends string>(
style: StyleRules<ClassKey> | StyleRulesCallback<ClassKey>,
options?: WithStylesOptions
): StyledComponentDecorator<ClassKey>; Requires type assertion or type definition:
import * as React from 'react';
import { withStyles, WithStyles } from 'material-ui/styles';
import { StyledComponent } from 'material-ui';
type C = 'root' | 'foo';
interface P { options?: string[]; }
interface S { open: boolean; }
export default withStyles((theme) => ({
root: {},
foo: {},
}))(class extends React.Component<P & WithStyles<C>, S> {
render() {
return (
<div className={this.props.classes.root} />
);
}
}) as StyledComponent<P, C>; // type assertion
import * as React from 'react';
import { withStyles, WithStyles } from 'material-ui/styles';
import { StyledComponent } from 'material-ui';
type C = 'root' | 'foo';
interface P { options?: string[]; }
interface S { open: boolean; }
const Component = withStyles((theme) => ({
root: {},
foo: {},
}))(class extends React.Component<P & WithStyles<C>, S> {
render() {
return (
<div className={this.props.classes.root} />
);
}
});
export default Component as StyledComponent<P, C>; // type assertion
const Component: StyledComponent<P, C> = withStyles((theme) => ({ // type definition
root: {},
foo: {},
}))(class extends React.Component<P & WithStyles<C>, S> {
render() {
return (
<div className={this.props.classes.root} />
);
}
});
export default Component;
import * as React from 'react';
import { withStyles, WithStyles } from 'material-ui/styles';
import { StyledComponent } from 'material-ui';
type C = 'root' | 'foo';
interface P { options?: string[]; }
interface S { open: boolean; }
@withStyles((theme) => ({
root: {},
foo: {},
}))
class Component extends React.Component<P & WithStyles<C>, S> {
render() {
return (
<div className={this.props.classes.root} />
);
}
}
export default Component as StyledComponent<P, C>; // type assertion |
@chilbi I user your hook for decorator and getting error |
i'm usage like: import { style } from './dayCard.style';
interface Props {
classes?: {
container: string,
pager: string,
toolbox: string,
animateWaper: string,
};
}
@withStyles(style)
export default class Container extends React.Component<Props, object> {
...
render () {
const { classes } = this.props;
return (
<div className={classes.container}>
<QueueAnim className={classes.animateWaper} type="bottom">
<Paper className={classes.pager} key="a">
<Paper className={classes.toolbox} key="b">
.... Container.style.ts import { StyleRules } from 'material-ui/styles';
const animateWaper = {
display: 'flex',
flex: 1,
};
export const style = (theme: any): StyleRules => ({
container: {
flex: 1,
display: 'flex',
padding: '3px 25px 0 0',
},
animateWaper,
pager: {
flex: 1,
},
toolbox: {
height: '64px',
display: 'flex',
alignItems: 'center',
},
}); Maybe not so good, but works!! |
@zhougonglai It doesn't work with v1.0.0-beta.16 |
@pelotom this example is use 'single' class type. like |
I wanted to ask about the recommended way to go forward using MenuItem's component with TypeScript. In particular regarding this feature of MenuItem:
Consider this code: import { Link } from 'react-router-dom';
/// ...
<MenuItem component={Link} to="/about">
About page
</MenuItem> It will complain about prop Update: I ended up solving this, but with too much ceremony, so I'd still like to know if there's a more succinct way. import { Link } from 'react-router-dom';
import MenuItem, { MenuItemProps } from '@material-ui/core/MenuItem';
interface LinkItemProps extends MenuItemProps {
to?: string,
}
const LinkItem: React.ReactType<LinkItemProps> = MenuItem;
<LinkItem component={Link} to="/about">
About page
</LinkItem> |
@gnapse People have already been wondering about this point. I don't know any better answer than: import { Link } from 'react-router-dom';
/// ...
<MenuItem component={props => <Link {...props} to="/about" />}>
About page
</MenuItem> |
Oh, so component does no need to be a component class, it can also be a render prop? Nice! Update: @oliviertassinari I tried it and though it works in the sense that TypeScript does not complain anymore, my menu items with links do not work anymore :( Maybe it's a problem with my codebase, I'll try to investigate further to rule out it's the library. |
It’s not treated as a render prop though, it’s treated as a (stateless functional) component. The implication of that is important to understand, because if you pass it a function inline like that, it will be treated as a brand new component on every render, which means that all components below |
Well, it's not working.
Gives error:
I don't understand, why this issue was closed. |
@Defite @oliviertassinari Same here. Do we have any insight as to why below pattern does not work?
|
@boraturant I think it is similar to this issue microsoft/TypeScript#16019 (comment) |
There is a problem across the board with the typing of the overriding export interface MenuItemProps extends StandardProps<ListItemProps, MenuItemClassKey> {
component?: React.ReactType<MenuItemProps>;
// ...
} The particular issue you're having is that it thinks the <MenuItem component={({ innerRef, ...props }) => <Link {...props} to="/about" />} /> There is a PR which would drastically improve the typing of components when overriding the <MenuItem component={Link} to="/about" /> as we'd all prefer; unfortunately that PR has hit a bit of a snag in the type system... |
@gnapse Where does this
|
@thril sorry, it should be const LinkItem: React.ReactType<LinkItemProps> = MenuItem; I updated my comment accordingly. Thanks for pointing this out. |
@gnapse your solution does not work for me: import * as React from 'react';
import Button, { ButtonProps } from '@material-ui/core/Button';
import { Link } from 'react-router-dom';
interface LinkButtonProps extends ButtonProps {
to?: string;
}
const LinkButton: React.ReactType<LinkButtonProps> = Button;
const C = () => {
return (
<div>
<LinkButton
variant="contained"
color="primary"
component={Link}
to="/whatever"
>
Whatever
</LinkButton>
</div>
);
};
export { C }; Still gives me:
|
Following this suggestions by @oliviertassinari, let's add some information about using TypeScript with
material-ui
. I would love to hear from folks what issues they ran into when they start using thev1
branch.I'll update this post with suggestions and create a separate PR containing an initial guide for the TS usage 🙂
Stuff to include in the guide:
@types/material-ui
).withStyles
as decorator / or why you really can't.The text was updated successfully, but these errors were encountered: