在肯定不应该为操作指定“永不”类型的情况下

时间:2018-11-03 21:22:13

标签: typescript redux

免责声明:我正在使用https://github.com/iRath96/electron-react-typescript-boilerplate

中的电子样板

创建者在其中编写了一些不错的动作代码(从样板中复制粘贴以获得更好的上下文:)

import { Action } from 'redux'

export interface IAction extends Action {}
export interface IActionWithPayload<T> extends IAction {
  readonly payload: T
}

interface IActionCreator<T> {
  readonly type: string
  (payload: T): IActionWithPayload<T>

  test(action: IAction): action is IActionWithPayload<T>
}

interface IActionCreatorVoid {
  readonly type: string
  (): IAction

  test(action: IAction): action is IAction
}

export const actionCreator = <T>(type: string): IActionCreator<T> =>
  Object.assign((payload: T): IActionWithPayload<T> => ({ type, payload }), {
    type,
    test(action: IAction): action is IActionWithPayload<T> {
      return action.type === type
    },
  })

export const actionCreatorVoid = (type: string): IActionCreatorVoid =>
  Object.assign((): IAction => ({ type }), {
    type,
    test(action: IAction): action is IAction {
      return action.type === type
    },
  })

所以,要创建新动作,我只需要做:

export const fetchAllBackgroundsPending = actionCreatorVoid('FETCH_BACKGROUNDS_PENDING')
export const fetchAllBackgroundsSuccess = actionCreator<IBackground[]>('FETCH_BACKGROUNDS_SUCCESS')
export const fetchAllBackgroundsRejected = actionCreator<IAPIError>('FETCH_BACKGROUNDS_REJECTED')

它运行得非常完美:我的action.payload现在已经输入。

但是,如果我在化径器中使用了两个以上的分支,那么我的有效负载上将得到“从不”类型:

const backgroundsReducer = (
  state: IBackgroundsState = initialState,
  action: IAction
): IBackgroundsState => {
  if (fetchAllBackgroundsPending.test(action)) {
    return {
      ...state,
      isCollectionLoading: true,
    }
  }

  if (fetchAllBackgroundsSuccess.test(action)) {
    return {
      ...state,
      // action has a type of never!
      byId: mergeById(state, action.payload)
    }
  }

  return state
}

据我从文档中得知,TS提示我代码分支永远不会运行,但事实并非如此:如果我将console.log放在那儿,我就可以观察它们!所以,看来TS出了错...还是我呢?

我知道这里提供的代码看起来不像是最小的示例,因此,如果不清楚的话,请告诉我,我将尝试提出一个最小的回购来说明此问题。任何帮助表示赞赏!

1 个答案:

答案 0 :(得分:1)

fetchAllBackgroundsPending.test被声明为user-defined type guard

test(action: IAction): action is IAction

也就是说,您要告诉TypeScript,假设且仅当参数的类型为test时,IAction才返回true。由于action的{​​{1}}参数的类型为backgroundsReducer,因此如果IAction返回false(表明fetchAllBackgroundsPending.test(action)不是action),则IAction可能没有剩余的类型,因此它的类型为action

我相信您对never的真正需求是TypeScript does not yet support的“单面”类型防护。但是在这种情况下,只需将test中的test的返回类型更改为普通的IActionCreatorVoid(使boolean不再是用户定义的类型防护, ),因为“真实”情况并不能为该操作确定更具体的类型。我鼓励您针对您使用的样板项目提出问题。

虽然test的{​​{1}}方法原则上也应该是单方面的,但当前声明不太可能引起问题,因为在测试类型test的变量时, “ false”的情况只是将变量保留为IActionCreator; TypeScript当前尚无法从IAction中排除IAction