代码之家  ›  专栏  ›  技术社区  ›  Shorn

Typescript:为什么materialui“withStyles()”不能与显式构造函数一起工作?

  •  4
  • Shorn  · 技术社区  · 6 年前

    编辑:

    请注意,IntelliJ IDEA生成了不正确的构造函数。现已修复,请参阅: https://youtrack.jetbrains.com/issue/WEB-35178


    withStyles 与TypeScript结合使用的样式解决方案。

    我正在尝试根据 Popper documentation .

    问题是,如果我按照习惯的方式显式定义构造函数( (B) 在下面的代码中),则该行:

    export default withStyles(styles)(HelpComponent);
    

    ERROR in [at-loader] ./src/main/ts/screen/keyword/HelpComponent.tsx:150:35 
        TS2345: Argument of type 'typeof HelpComponent' is not assignable to parameter of type 'ComponentType<never>'.
      Type 'typeof HelpComponent' is not assignable to type 'StatelessComponent<never>'.
        Type 'typeof HelpComponent' provides no match for the signature '(props: never, context?: any): ReactElement<any> | null'.
    

    我唯一能够完成这项工作的方法是省略显式构造函数并将state定义为字段( (A)

    ?

    实际上,我不介意这样直接设置状态,它没有那么简单。我这样在构造函数外初始化状态,是不是就放弃了什么?

    归根结底,我只是不明白TypeScript的错误信息-有人能解释一下它想说什么吗?

    import {
      createStyles,
      Paper,
      Popper,
      Theme,
      WithStyles,
      withStyles
    } from '@material-ui/core';
    import * as React from 'react';
    import {ReactNode, SyntheticEvent} from 'react';
    import {EventUtil} from "appUtil/EventUtil";
    import {WarningSvg} from "component/svg-icon/WarningSvg";
    let log = require("log4javascript").getLogger("HelpComponent");
    
    export interface HelpComponentProps extends WithStyles<typeof styles> {
      children:ReactNode;
    }
    
    export interface HelpComponentState {
      open:boolean;
      arrowRef?: HTMLElement;
    }
    
    class HelpComponent extends React.Component<HelpComponentProps, HelpComponentState> {
      helpRef!: HTMLElement;
    
      // (A)
      state = {open: false, arrowRef: undefined};
    
      // (B)
      // constructor(props: HelpComponentProps, context: HelpComponentState){
      //   super(props, context);
      //   this.state = {open: false, arrowRef: undefined};
      // }
    
      handleClick = (event:SyntheticEvent<any>) => {
        EventUtil.stopClick(event);
        this.setState({open: !this.state.open,});
      };
    
      handleArrowRef = (node:HTMLElement) => {
        this.setState({
          arrowRef: node,
        });
      };
    
      render(){
        const {classes} = this.props;
        return <span ref={(ref)=>{if(ref) this.helpRef = ref}}>
          <WarningSvg onClick={this.handleClick}/>
          <Popper id={"help-popper"} className={classes.popper} transition
            open={this.state.open} anchorEl={this.helpRef}
            modifiers={{arrow:{enabled:true, element: this.state.arrowRef}}}
          >
            <span className={classes.arrow} ref={this.handleArrowRef}/>
            <Paper className={classes.paper}>{this.props.children}</Paper>
          </Popper>
        </span>;
      }
    
    }
    
    const styles = (theme: Theme) => createStyles({
      root: {
        flexGrow: 1,
      },
      scrollContainer: {
        height: 400,
        overflow: 'auto',
        marginBottom: theme.spacing.unit * 3,
      },
      scroll: {
        position: 'relative',
        width: '230%',
        backgroundColor: theme.palette.background.paper,
        height: '230%',
      },
      legend: {
        marginTop: theme.spacing.unit * 2,
        maxWidth: 300,
      },
      paper: {
        maxWidth: 400,
        overflow: 'auto',
      },
      select: {
        width: 200,
      },
      popper: {
        zIndex: 1,
        '&[x-placement*="bottom"] $arrow': {
          top: 0,
          left: 0,
          marginTop: '-0.9em',
          width: '3em',
          height: '1em',
          '&::before': {
            borderWidth: '0 1em 1em 1em',
            borderColor: `transparent transparent ${theme.palette.common.white} transparent`,
          },
        },
        '&[x-placement*="top"] $arrow': {
          bottom: 0,
          left: 0,
          marginBottom: '-0.9em',
          width: '3em',
          height: '1em',
          '&::before': {
            borderWidth: '1em 1em 0 1em',
            borderColor: `${theme.palette.common.white} transparent transparent transparent`,
          },
        },
        '&[x-placement*="right"] $arrow': {
          left: 0,
          marginLeft: '-0.9em',
          height: '3em',
          width: '1em',
          '&::before': {
            borderWidth: '1em 1em 1em 0',
            borderColor: `transparent ${theme.palette.common.white} transparent transparent`,
          },
        },
        '&[x-placement*="left"] $arrow': {
          right: 0,
          marginRight: '-0.9em',
          height: '3em',
          width: '1em',
          '&::before': {
            borderWidth: '1em 0 1em 1em',
            borderColor: `transparent transparent transparent ${theme.palette.common.white}`,
          },
        },
      },
      arrow: {
        position: 'absolute',
        fontSize: 7,
        width: '3em',
        height: '3em',
        '&::before': {
          content: '""',
          margin: 'auto',
          display: 'block',
          width: 0,
          height: 0,
          borderStyle: 'solid',
        },
      },
    });
    
    export default withStyles(styles)(HelpComponent);
    

    • “typescript”:“3.0.3”
    • “@types/react dom”:“16.0.7”
    • “@material ui/core”:“3.1.1”
    • “网页包”:“4.19.1”
    1 回复  |  直到 6 年前
        1
  •  3
  •   Matt McCutchen    6 年前

    如果你看看 React.ComponentType

    type ComponentType<P = {}> = ComponentClass<P> | StatelessComponent<P>;
    

    您尝试使用的替代方法是 ComponentClass

    interface ComponentClass<P = {}, S = ComponentState> extends StaticLifecycle<P, S> {
        new (props: P, context?: any): Component<P, S>;
        // ...
    }
    

    请注意 context ? 上下文 构造函数的参数不是可选的,所以 HelpComponent 上下文 争论。如果将参数设置为可选,则错误应该会消失。

    当TypeScript报告错误时 typeof HelpComponent 不可分配给联合类型 ,它半任意地选择联合类型的一个成员来报告详细的错误。不幸的是,它没有选择您想要的,所以错误消息不是很有用。

    实际上,我不介意这样直接设置状态,它没有那么简单。我这样在构造函数外初始化状态,是不是就放弃了什么?

    state 属性,您可能无意中将其类型更改为与传递给 React.Component 基类。