【问题标题】:What does this Flow JS "polymorphic type instead of intersection type" error mean?这个Flow JS“多态类型而不是交集类型”错误是什么意思?
【发布时间】:2017-11-16 05:18:33
【问题描述】:

这个 Flow JS 错误是什么意思?

Expected polymorphic type instead of any member of intersection type.


详情:

错误是由Document 在下面的导出行中引发的。我不明白它为什么会发生,或者我能做些什么来解决它。 (我尝试在 Flow 文档中查找这一点,但仍然无法弄清楚。)

// @flow
import Document, {Head, Main, NextScript} from 'next/document'

export default class IntlDocument extends Document {
  static async getInitialProps (context) {
    const props = await super.getInitialProps(context)
    const {req: {localeDataScript}} = context
    return {
      ...props,
      localeDataScript
    }
  }

  render () {
    return (
      <html>
        <Head />
        <body>
          <Main />
          <script
            dangerouslySetInnerHTML={{
              __html: this.props.localeDataScript
            }}
          />
          <NextScript />
        </body>
      </html>
    )
  }
}

next/document 类型声明是这样的:

declare module "next/document" {
  import type {Component} from 'react';

  declare type Context = {
    pathname: string,
    query: any,
    req?: any,
    res?: any,
    xhr?: any,
    err?: any,
  };
  declare export var Head: Class<Component<void, *, *>>;
  declare export var Main: Class<Component<void, *, *>>;
  declare export var NextScript: Class<Component<void, *, *>>;
  declare export default Class<Component<void, *, *>> & {
    getInitialProps: (ctx: Context) => Promise<*>;
    renderPage(cb: Function): void;
  };
}

【问题讨论】:

    标签: javascript flowtype next.js


    【解决方案1】:

    首先我想指出,您提供的代码需要一些更新才能与 Flow v0.53.0 或更高版本一起使用。该版本对 React 的 Flow 注释的工作方式进行了重大更改。特别是 Component 类更改为采用两个类型参数而不是三个。我在回答中对代码 sn-ps 进行了必要的更改。

    在最新版本的 Flow (v0.59.0) 中,错误消息报告了更多详细信息:

    Error: react.js:7
      7: export default class IntlDocument extends Document {
                                                   ^^^^^^^^ identifier `Document`. Expected polymorphic type instead of any member of intersection type
      7: export default class IntlDocument extends Document {
                                                   ^^^^^^^^ intersection
      Member 1:
       15:   declare export default Class<Component<*, *>> & {
                                    ^^^^^^^^^^^^^^^^^^^^^^ class type: type application of Component. See lib: types/next-document.js:15
      Error:
        9:     const props = await super.getInitialProps(context)
                                         ^^^^^^^^^^^^^^^ property `getInitialProps`. Property not found in
        9:     const props = await super.getInitialProps(context)
                                   ^^^^^ statics of React$Component
      Member 2:
                                                             v
       15:   declare export default Class<Component<*, *>> & {
       16:     getInitialProps: (ctx: Context) => Promise<*>;
       17:     renderPage(cb: Function): void;
       18:   };
             ^ object type. See lib: types/next-document.js:15
      Error:
        7: export default class IntlDocument extends Document {
                                                     ^^^^^^^^ identifier `Document`. Expected polymorphic type instead of
                                                             v
       15:   declare export default Class<Component<*, *>> & {
       16:     getInitialProps: (ctx: Context) => Promise<*>;
       17:     renderPage(cb: Function): void;
       18:   };
             ^ object type. See lib: types/next-document.js:15
    
    
    Found 1 error
    

    有问题的交集类型是Document类型("next/document"的默认导出),是一个交集

    Class<Component<void, *, *>>
    

    {
      getInitialProps: (ctx: Context) => Promise<*>;
      renderPage(cb: Function): void;
    }
    

    看起来意图是 Document 是 React 的 Component 类的子类,具有静态方法 getInitialPropsrenderPage。 Flow 确实将getInitialPropsrenderPage 视为可以在Document 上调用的方法。问题是 Flow 没有将它们视为子类化的静态方法。所以你对super.getInitialProps(context) 的调用失败了。

    解决此问题的一种简单方法是将调用 super.getInitialProps(context) 更改为 Document.getInitialProps(context)

    更好的解决方法是用"next/document" 类型定义中的正确类声明替换类型交集:

    declare export default class Document<Props, State = void> extends React$Component<Props, State> {
      static getInitialProps(ctx: Context): Promise<Props>;
      static renderPage(cb: Function): void;
    }
    

    我明白为什么原作者不这样做了。该类型定义文件像这样导入Component 类型:

    import type {Component} from 'react';
    

    在 Flow 中,类是同名的值和类型。您需要该值来创建子类,因为子类是运行时存在的东西,而类型在运行时不存在。该导入引入了 Component 类型,但没有引入值,如果值不在范围内,Flow 将不允许您声明子类。如果您尝试,您将收到如下错误:

    types/next-document.js:26
     26:   declare export default class Document<Props, State = void> extends Component<Props, State> {
                                                                              ^^^^^^^^^ Component. type referenced from value position
      2:   import type {Component} from 'react';
                        ^^^^^^^^^ type Component
    

    原作者只导入了类型,因为类型定义文件不允许导入值——它们只能导入类型。作者发现他们无法正确声明子类,于是求助于该类型联合解决方法。

    恰好 React 的类型定义(内置于 Flow 中,请参阅 react.js)将一些类型放入全局命名空间。这是类型定义作者有时由于在类型定义文件中导入内容的问题而使用的一种解决方法。约定是使用大写的模块名称和美元符号作为全局范围类型的前缀。所以你可以在类型定义文件中使用React$Component类型而不需要导入它;这就是我在上面的声明中所做的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-06
      • 1970-01-01
      • 2015-01-03
      • 2013-07-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多