流:不是多态类型

Phi*_*ann 6 javascript static-analysis reactjs flowtype

我只是第一次集成流来静态检查我的JavaScript源。

我正在努力寻找错误流,但我无法自行解决。关于使用es6类和继承。更具体地说,我创建了一些react组件,它们应该继承一些方法。

我有一个标注组件,它表示未指定严重性的标注消息。为了使事情更简单一些,我想到了提供一个ErrorMessage组件,该组件继承了Callout组件。我的课程结构看起来像:

React.Component
    > AbstractComponent (here i add some project-wide helpers for i18n and so on
        > Callout (this represents a pretty message on the screen)
            > ErrorMessage (this represents an error)
Run Code Online (Sandbox Code Playgroud)

流程告诉我:

Error ?????????????????????????????????????????????????????????????????? src/js/Components/Generic/ErrorMessage.js:14:43

statics of Callout [1] is not a polymorphic type.

     11?     icon: string
     12? };
     13?
 [1] 14? export default class ErrorMessage extends Callout<Props> {
     15?
     16?     static get defaultProps(): Props {
     17?         return {
Run Code Online (Sandbox Code Playgroud)

该部分Callout<Props>突出显示

我已经为标注类定义了道具类型,所以这可能是问题所在,但我不能自己解决。

下面几行引发了类似的错误,在该行中,我尝试通过解决super.content(内容是Callout的get-method)来访问父方法。

提前致谢

更新:为什么要使用类继承?

继承Callout> ErrorMessage只是为了减少冗余代码而存在,但是它不是必需的,因此让我们忽略它并讨论一个更常见的情况:

我想开一门课AbstractComponent,使我项目中的普通事情变得容易。

一些例子:

打印翻译字符串:为了使组件具有多种语言,我在组件内部创建了一个实用程序类来生成翻译字符串,它的工作原理类似于

function render() {
    return (
        <div>
            {new Translation(
                'namespace',
                'key',
                [some, args],
                `${some} fallback message with optional ${args}`
            ).toString()}
        </div>
    )
}
Run Code Online (Sandbox Code Playgroud)

为了使用此功能,堆栈中的每个组件最终都将import语句放在顶部

import Translation from "../Core/Translation"
Run Code Online (Sandbox Code Playgroud)

或最好的情况

import t from "../Core/Translation"
Run Code Online (Sandbox Code Playgroud)

我使用webpack构建了一个包,并且webpack似乎在您使用的每个import语句中炸毁了已编译的javascript。所以我想-为了减少编码工作量和捆束大小-我提供了一个中间组件类,它添加了一些实用程序方法,例如:

class AbstractComponent extends React.Component {

    constructor(props) {
        super(props);
        this.logger = props.logger || new Logger();
        this.api: ApiInterface = props.api || new MockApi();
    }

    translate(namespace: string, key: string, args: ?[] = null, fallback: ?string): string {
        return new Translation(namespace, key, args, fallback).toString();
    }

    svgSprite(id: string, className: string = "") {
        return (
            <SvgSprite id={id} className={className} />
        )
    }

}
Run Code Online (Sandbox Code Playgroud)

我还添加了其他一些东西,以向您展示更多使用中间Component类的原因。

因此,所有这些工作!但是flow抱怨缺少返回类型等等,这对我来说很好,为此,我想使用flow!我无法解决的问题是继承本身。但是对我来说,这确实很有意义。

kal*_*ley 3

如果您确实想处理继承(我对此没有问题,我只是觉得您以后可能会遇到问题),您可以执行以下操作:

class AbstractComponent<Props: {}, State: ?{} = null> extends React.Component<Props, State> {
    api: ApiInterface

    logger: typeof Logger

    constructor(props) {
        super(props);
        this.logger = props.logger || new Logger();
        this.api = props.api || new MockApi();
    }

    translate(namespace: string, key: string, args: ?string[] = null, fallback: ?string): string {
        return new Translation(namespace, key, args, fallback).toString();
    }

    svgSprite(id: string, className: string = "") {
        return (
            <SvgSprite id={id} className={className} />
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

class Test extends AbstractComponent<{ some: string, args: string }> {
  render() {
    const { some, args } = this.props
     return (
        <div>
            {this.translate(
                'namespace',
                'key',
                [some, args],
                `${some} fallback message with optional ${args}`
            )}
        </div>
    )    
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,我想说,在某种程度上我理解 Facebook 的由来。在这种情况下,您的组件实际上已经是一个抽象构造。如果您希望它更加灵活(假设您有一个无状态组件,可以从 alogger和 atranslate函数中受益),您可以执行以下两件事之一:

这是我在两者中使用的定义类型和翻译函数:

type CommonProps = {
  logger?: Logger,
  api?: ApiInterface,
  translate?: (namespace: string, key: string, args: ?string[], fallback: ?string) => string
}

// This should look familiar
function translate(namespace: string, key: string, args: ?string[] = null, fallback: ?string): string {
    return new Translation(namespace, key, args, fallback).toString();
}
Run Code Online (Sandbox Code Playgroud)

高阶分量

function addCommonStuff({ logger = new Logger(), api = new MockApi(), translate = translate }: CommonProps) {
  return <Props: {}>(
    WrappedComponent: ComponentType<Props>
  ): ComponentType<
    $Diff<Props, $NonMaybeType<CommonProps>>
  > => (props: Props) => <WrappedComponent {...props} logger={logger} api={api} translate={translate} />
}
Run Code Online (Sandbox Code Playgroud)

并使用如下:

class Test extends React.Component<{}> {}

const TestWithCommons = addCommonStuff({})(Test)

;<TestWithCommons /> 
Run Code Online (Sandbox Code Playgroud)

带有 render prop 的可重用组件

class Common extends React.Component<CommonProps & { render?: Function, children?: Function }, $NonMaybeType<CommonProps>> {
  state = {
    logger: this.props.logger || new Logger(),
    api: this.props.api || new MockApi(),
    translate: translate
  }

  render() {
    const { children, render } = this.props

    return typeof render === 'function' ? render(this.state) : (
      typeof children === 'function' ? children(this.state) : null
    )
  }
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

class TestCommon extends React.Component<{}> {
   render() {
     return <Common>
       {({ logger, api, translate }) => translate('namespace',
        'key',
        null,
        `Fallback message`
      )}
    </Common>
   }
}
Run Code Online (Sandbox Code Playgroud)

作为本次讨论的旁白,您不需要defaultProps为标注编写 getter。static defaultProps = {}应该足够了。它不应该考虑传入的 props 或任何东西。如果是这样,你最好使用state