flow
React
redux

react-reduxのconnectでFlowの"call of `connect`. Could not decide which case to select"エラーが出てしまう場合の対処法

再現手順

  • 環境: flow v0.55.0以上

例として以下のReact Componentを作成しFlowの型チェックを行うと
call of connect. Could not decide which case to select
エラーが発生します。

App.js
// @flow
import React, { Component } from 'react'
import { connect } from 'react-redux'

type Props = {
  name: string
}

class App extend Component<Props> {
  render() {
    const { name } = this.props

    return <div>{name}</div>
  }
}

const mapStateToProps = state => {
  return {
    name: state.name
  }
}

export connect(mapStateToProps)(App)

エラーの全文
 75: export default connect(mapStateToProps)(App)
                    ^^^^^^^^^^^^^^^^^^^^^^^^ call of `connect`. Could not decide which case to select
 75: export default connect(mapStateToProps)(App)
                    ^^^^^^^ intersection type
  Case 3 may work:
                                 v--------------
   98:   declare function connect<S, A, OP, SP>(
   99:     mapStateToProps: MapStateToProps<S, OP, SP>,
  100:     mapDispatchToProps: Null,
  ...:
  103:   ): Connector<OP, $Supertype<SP & { dispatch: Dispatch<A> } & OP>>;
         ----------------------------------------------------------------^ function type. See lib: flow-typed/npm/react-redux_v5.x.x.js:98
  But if it doesn't, case 5 looks promising too:
                                 v------------------
  112:   declare function connect<S, A, OP, SP, DP>(
  113:     mapStateToProps: MapStateToProps<S, OP, SP>,
  114:     mapDispatchToProps: MapDispatchToProps<A, OP, DP>,
  ...:
  117:   ): Connector<OP, $Supertype<SP & DP & OP>>;
         -----------------------------------------^ function type. See lib: flow-typed/npm/react-redux_v5.x.x.js:112
  Please provide additional annotation(s) to determine whether case 3 works (or consider merging it with case 5):
   69: function mapStateToProps(state) {
                                      ^ return

解決方法

mapStateToPropsに型定義を追加しましょう。MapStateToProps<*, *, *>
事前にflow-typed installコマンドでFlow型定義ファイルを取得する費用があります。

App.js
// @flow
import React, { Component } from 'react'
import { connect } from 'react-redux'
+ import type { MapStateToProps } from 'react-redux'

type Props = {
  name: string
}

class App extend Component<Props> {
  render() {
    const { name } = this.props

    return <div>{name}</div>
  }
}

+ const mapStateToProps: MapStateToProps<*, *, *> = state => {
  return {
    name: state.name
  }
}

export connect(mapStateToProps)(App)

原因

connect()の型定義は引数の数に応じて複数のシグネチャが定義されており、
Javaのオーバーロードのような振る舞いをしている。

react-redux_v5.x.x.x.js
  declare function connect<A, OP>(
    mapStateToProps: Null,
    mapDispatchToProps: Null,
    mergeProps: Null,
    options: ConnectOptions
  ): Connector<OP, $Supertype<{ dispatch: Dispatch<A> } & OP>>;

  declare function connect<S, A, OP, SP>(
    mapStateToProps: MapStateToProps<S, OP, SP>,
    mapDispatchToProps: Null,
    mergeProps: Null,
    options?: ConnectOptions
  ): Connector<OP, $Supertype<SP & { dispatch: Dispatch<A> } & OP>>;

  declare function connect<A, OP, DP>(
    mapStateToProps: Null,
    mapDispatchToProps: MapDispatchToProps<A, OP, DP>,
    mergeProps: Null,
    options?: ConnectOptions
  ): Connector<OP, $Supertype<DP & OP>>;

  declare function connect<S, A, OP, SP, DP>(
    mapStateToProps: MapStateToProps<S, OP, SP>,
    mapDispatchToProps: MapDispatchToProps<A, OP, DP>,
    mergeProps: Null,
    options?: ConnectOptions
  ): Connector<OP, $Supertype<SP & DP & OP>>;

最初のコードだと第一引数に渡したmapStateToProps変数の型が未指定のため、
どのシグネチャでコールされるされるのか確定していない。

そのため引数の型を明示し、特定のシグネチャへ固定するためのヘルプメッセージが出力されている。

参考

https://github.com/flowtype/flow-typed/issues/1269#issuecomment-332100335