Flow: [Flow 0.85] How to type connected components with react-redux?

Created on 31 Oct 2018  Â·  12Comments  Â·  Source: facebook/flow

As announced in this blog post, Flow 0.85 needs extra type annotations for some exports.

This seems to be the case for react-redux connected components.

Here is an example:
https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVBLAtgBzgJwBdkwBDAZzACUBTUgY2KnzizAHJ87H2BudYMDDlsOGBigYaAEzBcGhALRdpAVwAeYQgE8cNcumk16MUly26aYACIZyOUoXoALAIIAFAJIAeVwD4wAF4wAAoFDDgAOwAuMFcASiCA134jEzMrHT0bOwcnZ19YgG8smliAEgBlVQAjUu9yQnwMSIBzPwBfAODbe0cXDx9-flQ003NSsABZUhxKwkcaABU4dxYccm9K2IB5GoArY0IAGjBK912Do9Oqc8vDxm7QxsXYytOcdfI390TAgNu7hGYwyYCgqkijAikTA9CikSO3lQYDAAGFWLFaAoAHTo3BRGiRQhLSzeEB+Y7Is73a5Uu5gPYPE5UwE0xiUlGoi5gcq2KBQbxYxjYgCiMBoWEJhHRkUkrW8eIp1HOFLpS2KAG0APoVADSNG0m0VAF1YqRItoOqg-CEqVhZvNFis1nANrEZnMFoRlqsvltTucbiqOWB7ThenkXM6vgB+WKRVQwGCoeKxEJw-EIomxPF-AHcQi41h4LPE0lcsAAMjO7gC1aqzlmNC2Sz8I0EYEJsjgUDkBeUMg0Fj0BnQkxdGyCYCKAB8qTh46osDUaPgQy0mTJuQnl6vUDOOiM4ZFGmjiwSiVPQkUcKcN0ctx1YhPyHnQlTvNIMAA3Pw3jpkJEshFPejCPt4wBfr+YApugx6nmGjretGrqUMEIRviERRUqB3rSNyADMqAdPER5RKeMoImB0h4jgU7HlRhAhIhXo+i+8QhLRF6EKR6AhBhSTvii3h+FSwlcaWYA4IERQAIwAbhW4yQATABwCiSi4nwg+NHFlJMnyWA6kfsZ8QcfE6A0OoeBEGARhQKQibEJROm0fwmkeZ5XneWAQA

It fails with these two errors in Flow 0.85, but worked fine in Flow 0.84

Missing type annotation for `CP`. `CP` is a type parameter declared in function type [1] and was implicitly instantiated at call of `connect` [2].

and

Missing type annotation for `SP`. `SP` is a type parameter declared in function type [1] and was implicitly instantiated at call of `connect` [2]

So what's the recommended way to type connected components with react-redux and Flow 0.85?

Most helpful comment

Whenever I think I'm starting to get flow, stuff like this breaks my brain 😅
I have no idea how to fix my 300 connect errors right now. Giving them 6 parameters doesn't really sound like the most readable thing to do.

All 12 comments

Not sure if I should open an issue to flow-typed instead

See my response to this question on the Medium post.

The simplest thing is to add an explicit type argument to the call of connect, which is at the location pointed to by the error message in your example. The specific types you provide depend on the signature of the function.

I suspect that the flow-typed folks might want to rework the type declarations to make it simpler to provide these annotations. I suggested one approach in my comment on Medium. The details of the specific declaration should be taken up with the author, so I suspect that a question to the flow-typed repo would be a good step.

@samwgoldman, thanks a bunch for providing those concise and strict typings for connect I personally was missing forever!

Imho, there is a little typo in the typings:

-  mapStateToProps: MapStateToProps<S, OP, DP>,
+  mapStateToProps: MapStateToProps<S, OP, SP>,
-  mapDispatchToProps: MapDispatchToProps<D, OP, SP>,
+  mapDispatchToProps: MapDispatchToProps<D, OP, DP>,

This fixes an error like this:

[Flow] Cannot call connect with mapStateToProps bound to mapStateToProps because property someDispatchProperty is missing in StateProps but exists in DispatchProps [2] in the return value.

By the way, for those type hackers spending their weekends in fixing 100 new (legitimate) errors, here is how I use a bit modified version of Sam's typings.

First I split the huge all in one Props type in three distinct types (OwnProps, StateProps, DispatchProps) and one combined (just Props):

// these are the old school props coming from component usage:
// <Component own1="foo" own2="bar" />
type OwnProps = {|
  own1: string,
  own2: number,
|};

// these are the props coming from state via `mapStateToProps`
type StateProps = {|
  state1: boolean,
  state2: number,
|};

// these are the props dispatching actions from `mapDispatchToProps`
type DispatchProps = {|
  action1: typeof actionCreator1, // actionCreator1 is an imported action creator function
  action2: typeof actionCreator2, // actionCreator2 is an imported action creator function
|};

// and finally the main type for component type creation (`Component<Props, …>`, see below)
type Props = {|
    ...OwnProps,
    ...StateProps,
    ...DispatchProps,
|};

// here is the normal React component class definition
class MyComponent extends Component<Props, void> {
  render() {
    // `this.props` has all the props as expected because it's of type `Props`
    console.log(this.props.own1)
    console.log(this.props.own2)
    console.log(this.props.state1)
    console.log(this.props.state2)
    // including action dispatchers
    console.log(this.props.action1(…))
    console.log(this.props.action2(…))
  }
}

// and the most important part is using the `connect()` function with proper type parameters
export const MyController = connect<State, Dispatch, OwnProps, StateProps, DispatchProps>(
    mapStateToProps,
    mapDispatchToProps,
)(MyComponent);

The State and Dispatch types come from how it's officially recommended to type Redux with Flow. In short:

// the overall state of your app
type State = {|
  +forms: FormsReducerState,
  +notifications: NotificationsReducerState,
  // and so on
|}

// all the actions of your app
type Action = FormsAction1 | FormsAction2 | NotificationsAction1 | NotificationsAction2;

// type of a `dispatch()` function
type Dispatch = Action => Action

P.S. Adding this code to connect() typings allows to use an object with dispatchers instead of a function:

+ declare export function connect<S, D, OP, SP, DP>(
+     mapStateToProps: MapStateToProps<S, OP, SP>,
+     mapDispatchToProps: DP,
+ ): Connector<S, D, OP, React$ComponentType<{| ...OP, ...SP, ...DP |}>>;

Notice, that I use a bit modified version of Sam's typings. Patches are welcome :)

EDIT: as stated in Sam's comment it is also possible to use _ type placeholder in place of StateProps and DispatchProps arguments in the call to connect() to make Flow infer the types for you:

export const MyController = connect<State, Dispatch, OwnProps, _, _>(
    mapStateToProps,
    mapDispatchToProps,
)(MyComponent);

but his trick makes error messages a bit more verbose and possible make Flow less performant. Something like how it was with the Existential Type (*).

Whenever I think I'm starting to get flow, stuff like this breaks my brain 😅
I have no idea how to fix my 300 connect errors right now. Giving them 6 parameters doesn't really sound like the most readable thing to do.

I wonder if the should be a way to tell Flow "I know what I'm doing" and make it possible to export a generic instantiation, like $FlowFixMe but for performance related things. Kinda $FlowTrustMe :D

My sad face when Flow is already at 0.88 but we can't get past 0.85 without $FlowFixMe-fying 500 errors...

This feels pretty bad. Why was this closed?

@oshalygin I guess, because of these two PRs to flow-typed: https://github.com/flow-typed/flow-typed/pull/3012 and https://github.com/flow-typed/flow-typed/pull/3035, we continued the party there.

We are also crafting another one to make the connect() function typings more composable with other HOC (in terms of compose() function from Redux) here: https://github.com/flow-typed/flow-typed/pull/3076. Please, help 😊

Thanks for the update @peter-leonov !

@kangax As far as I understand it should be still possible to type via:

connect<*, *, *, *, *>

So I think, running replace of connect( with connect<*, *, *, *, *>( may solve part of your issues.

Was this page helpful?
0 / 5 - 0 ratings