ownProps,mapStateToProps和mapDispatchToProps的react-redux流类型定义

时间:2018-04-16 00:09:32

标签: reactjs redux react-redux flowtype

我是使用flow的新手,所以我可能会做一些非常愚蠢的错误。

我想创建一个Props流类型,它与传递给组件的props相交,从mapStateToProps返回的redux商店获取的props和mapDispatchToProps上定义的一些动作创建者。

以下所示的所有代码均可在此flow try上找到。

让我们考虑一下,我有以下状态和动作创建者:

州和行动创作者

// State type
type State = {
    state_text: string,
    state_num: number,
}

// Action type
type Action = 
    | { type: 'ACTION_CREATOR_1', text: string}
    | { type: 'ACTION_CREATOR_2', value: number}


// action creators
const actionCreator_1 = (): Action => ({
    type: 'ACTION_CREATOR_1',
    text: 'some text',
})
const actionCreator_2 = (num: number): Action => ({
    type: 'ACTION_CREATOR_2',
    value: num + 1,
})

我在this帖子上关注了一个示例,我得到了类似下面的内容来定义我的类型:

道具类型定义

const mapStateToProps = (state: State) => {
    const { state_text, state_num } = state
    return { state_text, state_num }
}

const mapDispatchToProps = {
    actionCreator_1,
    actionCreator_2,
}


// type extract return helper
type _ExtractReturn<B, F: (...args: any[]) => B> = B
type ExtractReturn<F> = _ExtractReturn<*, F>


// set combined types 
type ReduxState = ExtractReturn<typeof mapStateToProps>

type ReduxActions = typeof mapDispatchToProps

type OwnProps = { 
    own_string: string,
    own_num: number 
}

type Props =  OwnProps & ReduxState & ReduxActions

根据这个定义,我期待Props是这样的:

type DesiredProp =   {|
    own_string: string,
    own_num: number,
    state_text: string,
    state_num: number,
    actionCreator_1: () => Action,
    actionCreator_2: (num: number) => Action,
|} 

但是,以下情况显示没有流量错误:

const props2: Props = {
    own_string: 'text',                 // OK - gives error if 123  
    own_num: 123,                       // OK - gives error if 'text'
    state_text: 123,                    // NOK - should be an error     
    state_num: 'text',                  // NOK - should be an error 
    actionCreator_1: actionCreator_1,   // OK - gives error if actionCreator_2
    actionCreator_2: actionCreator_1,   // NOK - should be an error 
    fakeActionCreator: () => {}         // NOK - should be an error 
}

我将所有这些代码放在flow try中,以便更容易理解和使用。

我如何实现我正在寻找的目标?

1 个答案:

答案 0 :(得分:5)

好吧,看来你的帖子中有一些主题,所以我打破了我的答案

返回不相交的联盟成员

此处您正在返回Action disjoint union

的成员
// Action type
type Action = 
  | { type: 'ACTION_CREATOR_1', text: string}
  | { type: 'ACTION_CREATOR_2', value: number}

// action creators
const actionCreator_1 = (): Action => ({
  type: 'ACTION_CREATOR_1',
  text: 'some text',
})

const actionCreator_2 = (num: number): Action => ({
  type: 'ACTION_CREATOR_2',
  value: num + 1,
})

你希望它能够对这些行进行类型检查:

actionCreator_1: actionCreator_1,   // OK - gives error if actionCreator_2
actionCreator_2: actionCreator_1,   // NOK - should be an error 

问题是,您将动作创建者的返回类型设置为Action,因此只要您的actionCreator_1 / 2属性之一返回Action,代码就是正确的。您需要做的是使动作创建者的返回类型更具体:

type Action1 = { type: 'ACTION_CREATOR_1', text: string}
type Action2 = { type: 'ACTION_CREATOR_2', value: number}

// Action type
type Action = 
  | Action1
  | Action2

// action creators
const actionCreator_1 = (): Action1 => ({
  type: 'ACTION_CREATOR_1',
  text: 'some text',
})

const actionCreator_2 = (num: number): Action2 => ({
  type: 'ACTION_CREATOR_2',
  value: num + 1,
})

现在流将抛出错误(try):

    76:     actionCreator_2: actionCreator_1,   // NOK - should be an error 
                            ^ Cannot assign object literal to `props2` because property `value` is missing in `Action1` [1] but exists in `Action2` [2] in the return value of property `actionCreator_2`.
    References:
    18: const actionCreator_1 = (): Action1 => ({
                                    ^ [1]
    23: const actionCreator_2 = (num: number): Action2 => ({
                                               ^ [2]
    76:     actionCreator_2: actionCreator_1,   // NOK - should be an error 
                            ^ Cannot assign object literal to `props2` because string literal `ACTION_CREATOR_1` [1] is incompatible with string literal `ACTION_CREATOR_2` [2] in property `type` of the return value of property `actionCreator_2`.
    References:
    9: type Action1 = { type: 'ACTION_CREATOR_1', text: string}
                              ^ [1]
    10: type Action2 = { type: 'ACTION_CREATOR_2', value: number}
                               ^ [2]

Exactly指定对象

现在您正在使用三个对象的交集来获取Props类型。由于您正在使用不精确的对象(交集的结果),因此您无法获得准确的结果。首先,您需要准确调整OwnProps。然后你需要结合所有对象。我这样做是为了传播:

// set combined types 
type ReduxState = ExtractReturn<typeof mapStateToProps>
type ReduxActions = typeof mapDispatchToProps

// This object type needs to be exact
type OwnProps = {|
  own_string: string,
  own_num: number 
|}

// Use a spread to combine the exact objects into one
type Props =  {|
  ...OwnProps,
  ...ReduxState,
  ...ReduxActions,
|}

现在它为那个讨厌的额外属性(try)抛出一个错误:

    74: const props2: Props = {                          ^ Cannot assign object literal to `props2` because property `fakeActionCreator` is missing in `Props` [1] but exists in object literal [2].
    References:
    74: const props2: Props = {
                      ^ [1]
    74: const props2: Props = {                          ^ [2]

使用推理提取的类型干扰

不幸的是,我似乎无法通过返回类型提取了解ReduxState的返回类型。它似乎认为是这样的:

type ReduxState = ExtractReturn<(state: State) => {|state_num: (string | number), state_text: (string | number)|}>

所以它似乎正在合并你在props2对象中使用的推断和提取的类型信息。为了解决这个问题,我最好的建议是手动输入mapStateToProps函数的输出:

const mapStateToProps = (state: State): {|
    state_text: string,
    state_num: number,
|} => {
  const { state_text, state_num } = state
  return { state_text, state_num }
}

之后它应该抛出错误(Try

    80:     state_text: 123,                    // NOK - should be an error     
                     ^ Cannot assign object literal to `props2` because number [1] is incompatible with string [2] in property `state_text`.
    References:
    80:     state_text: 123,                    // NOK - should be an error     
                     ^ [1]
    34:     state_text: string,
                     ^ [2]
    81:     state_num: 'text',                  // NOK - should be an error 
                      ^ Cannot assign object literal to `props2` because string [1] is incompatible with number [2] in property `state_num`.
    References:
    81:     state_num: 'text',                  // NOK - should be an error 
                      ^ [1]
    35:     state_num: number,
                       ^ [2]