使用React的Cryptic Typescript编译器错误:&#39; ComponentClass <styledcomponentprops <{}>&gt;&#39;不能分配给#Comp;类型的MyComponent&#39;

时间:2017-09-08 14:13:45

标签: reactjs typescript material-ui

我试图在React组件上使用Material UI 1.0(beta)@withStyles注释。文档提供了一个Javascript示例(https://material-ui-1dab0.firebaseapp.com/customization/css-in-js),但它在Typescript中给出了编译错误。它实际上发出正常,应用程序运行正常,所以我只是试图摆脱IDE的错误!

这是我的代码:

y

这会出错:

@withStyles(styles)
class MyComponent extends React.Component<any, any> {
    manager: any;
    ...
}

我真的不明白这个错误。我可以使用这样的替代非注释版本:

TS1238: Unable to resolve signature of class decorator when called as an expression.
 Type 'ComponentClass<StyledComponentProps<{}>>' is not assignable to type 'typeof MyComponent'.
 Type 'Component<StyledComponentProps<{}>, ComponentState>' is not assignable to type 'MyComponent'.
 Property 'manager' is missing in type 'Component<StyledComponentProps<{}>, ComponentState>'.

这个编译/运行没有问题。我喜欢使用注释,但我想了解错误。

任何建议表示赞赏!

2 个答案:

答案 0 :(得分:4)

这意味着declared type of the @withStyles decorator

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

实际上并不符合TypeScript对类装饰器的需求,这更像是:

declare function classDecorator(...args: any[]): <C> (c: C) => C;

合同非常严格:它要求函数输出为输入的子类型。这导致人们之前有problems

如果一切都在运行时工作正常,你只想让类型检查器保持沉默,那么总是很好any

@(withStyles as any)(styles) // no error now
class MyComponent extends React.Component<any, any> {
    manager: any;
    ...
}

或者,您可以继续尝试重新声明withStyles()的类型,使其对装饰者更友好:

import { StyledComponentProps } from 'material-ui';
import { Theme } from 'material-ui/styles/createMuiTheme';
import { StyleRules, StyleRulesCallback, WithStylesOptions } from 'material-ui/styles/withStyles';
import { withStyles } from 'material-ui/styles'
declare module 'material-ui/styles' {
  export 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>>;

}
@withStyles(styles) // no error now
class MyComponent extends React.Component<any, any> {
  manager: any;
}

现在一切正常,因为我确保返回的值是MyComponent构造函数的子类型。在你自己的项目中修复了很多别人的代码;我可能只是自己使用any解决方案。

是否有人想联系Material UI folks并建议更新定义取决于他们。我认为自己对React等人的知识不足。了解我的修改声明是否合适。

无论如何,希望有所帮助;祝你好运!

答案 1 :(得分:1)

我无法使用@ jcalz的类型定义,因为它与现有的定义冲突,所以我创建了一个包装函数。

classes上需要WithStyles也会让使用装饰组件变得很痛苦,所以我将其设为可选项。

// withStyles.ts

import { Theme } from 'material-ui/styles';
import _withStyles, {
  ClassNameMap,
  StyledComponentProps,
  StyleRules,
  StyleRulesCallback,
  WithStylesOptions,
} from 'material-ui/styles/withStyles';

export interface WithStyles<ClassKey extends string = string> {
  classes?: ClassNameMap<ClassKey>;
  theme?: Theme;
}

// We need to fix the withStyles definition and we want to make WithStyles.classes optional,
// so we make our own.
export const withStyles = <ClassKey extends string>(
  style: StyleRules<ClassKey> | StyleRulesCallback<ClassKey>,
  options?: WithStylesOptions
) => <C extends React.ComponentType<P & WithStyles<ClassKey>>, P = {}>(component: C) => {
  return (_withStyles as any)(style, options)(component) as C & React.ComponentType<P & StyledComponentProps<ClassKey>>;
};

要使用它,只需导入新的withStyles.ts而不是material-ui

import { withStyles, WithStyles } from './withStyles';

@withStyles(styles)
export class MyClass extends React.Component<MyClassProps> {
  render() {
    // classes will always exist so use `!`.
    const classes = this.props.classes!;

    return (
      <div className={classes.root} />
    );
  }
}