如何使用TypeScript键入Redux-Observable的史诗动作

问题描述

我正在我的应用程序中设置TypeScript和redux-observable,并具有以下简单流程,我想在此处使用Epic进行应用:

dispatch('someAction')
fetch('relevant-endpoint-from-api')
getResponse(dispatch('someActionSuccess'))
Failed(dispatch('someActionFailed'))

因此,在this article之后,我尝试了一下。

这是我的actions.ts文件

export interface MediasOrigin {
  id: number;
  label: string;
  url: string;
  zonesId: number[];
  scenariosRef: number[];
  stepsRef: number[];
  gearsId: number[];
  texturesId: number[];
}

export enum MediasActionTypes {
  MEDIAS_ORIGINS_FETCH_ALL = "MEDIAS_ORIGINS_FETCH_ALL",MEDIAS_ORIGINS_FETCH_ALL_SUCCESS = "MEDIAS_ORIGINS_FETCH_ALL_SUCCESS",MEDIAS_ORIGINS_FETCH_ALL_Failed = "MEDIAS_ORIGINS_FETCH_ALL_Failed"
}

export interface IMediasOriginsFetchAllAction {
  type: MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL;
}

export interface IMediasOriginsFetchAllSuccessAction {
  type: MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL;
  payload: {
    medias: MediasOrigin[]
  }
}

export interface IMediasOriginsFetchAllFailedAction {
  type: MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL;
  payload: {
    error: string
  }
}

/* ACTION CREATOR */
export function fetchAllMediaOrigins(): IMediasOriginsFetchAllAction {
  return {
    type: MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL
  }
}

export type MediasAction = IMediasOriginsFetchAllAction | IMediasOriginsFetchAllSuccessAction | IMediasOriginsFetchAllFailedAction

然后是我的epic.ts文件

import { from,Observable,of } from "rxjs"
import { map,catchError,switchMap,filter } from "rxjs/operators"
import { isOfType } from "typesafe-actions"
import { Epic } from "redux-observable"

import apiclient from "agent" // change,wrapper around superagent (http client)
import {IStore} from "reducers"
import {
  MediasAction,MediasActionTypes,IMediasOrigin
} from "./actions"

const getAllMediasOriginEpic: Epic<MediasAction,MediasAction,IStore> = action$ => {
  const request = new apiclient();

  return action$.pipe(
    filter(isOfType(MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL)),switchMap(action => // change
      from(request.getAllOriginMedias()).pipe( // change
        map((response: IMediasOrigin[]) // change =>
          of({
            type: MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL_SUCCESS,payload: { medias: response },})
        ),catchError((error: Error) // change =>
          of({
            type: MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL_Failed,payload: {error: Error.message},// change
          })
        )
      )
    )
  )
}

export default combineEpics(getAllMediasOriginEpic)

我是TypeScript的新手,所以对于狂热者来说,该错误似乎很明显,但是基于TypeScript的消息,我没有做错什么。

(不再相关),该消息指向史诗中返回的action$

Type 'Observable<IMediasOriginsFetchAllAction | Observable<{ type: MediasActionTypes; payload: { medias: unkNown; }; }>>' is not assignable to type 'Observable<MediasAction>'.
  Type 'IMediasOriginsFetchAllAction | Observable<{ type: MediasActionTypes; payload: { medias: unkNown; }; }>' is not assignable to type 'MediasAction'.
    Type 'Observable<{ type: MediasActionTypes; payload: { medias: unkNown; }; }>' is not assignable to type 'MediasAction'.
      Type 'Observable<{ type: MediasActionTypes; payload: { medias: unkNown; }; }>' is missing the following properties from type 'IMediasOriginsFetchAllSuccessAction': type,payload  TS2322

我没有解决该问题的办法,尽管从该消息看来,我所做的一切都是错误的,并且尽管在接口中将其指定为MediasOrigin的数组,但媒体是未知的。我在这里想念东西吗?

有关信息,以下是软件包的版本:

  • 打字稿:4.0.2
  • 可观察到的Redux:1.2.0
  • rxjs:6.6.3

//编辑:2020年9月24日

为了使其更简单,我进行了一些更改,并弄清了错误消息为什么谈论mediasUnkNown:我没有在史诗中告诉从api调用映射的响应是MediasOrigin[]重命名为IMediasOrigin []现在)。我也删除api方法,该方法是我自己的自定义运算符。上面的脚本已进行了更改,并带有// change注释。

现在是错误消息:

TypeScript error in /Users/utopiad/Dev/spectral-core/platform/src/components/Medias/epics.ts(11,7):
Type '(action$: ActionsObservable<MediasAction>) => Observable<Observable<{ type: MediasActionTypes; payload: { medias: IMediasOrigin[]; }; }> | { ...; }>' is not assignable to type 'Epic<MediasAction,IStore,any>'.
  Type 'Observable<Observable<{ type: MediasActionTypes; payload: { medias: IMediasOrigin[]; }; }> | { type: MediasActionTypes; payload: { ...; }; }>' is not assignable to type 'Observable<MediasAction>'.
    Type 'Observable<{ type: MediasActionTypes; payload: { medias: IMediasOrigin[]; }; }> | { type: MediasActionTypes; payload: { ...; }; }' is not assignable to type 'MediasAction'.
      Type 'Observable<{ type: MediasActionTypes; payload: { medias: IMediasOrigin[]; }; }>' is not assignable to type 'MediasAction'.
        Type 'Observable<{ type: MediasActionTypes; payload: { medias: IMediasOrigin[]; }; }>' is missing the following properties from type 'IMediasOriginsFetchAllFailedAction': type,payload

从我得到的结果来看,我的IMediasOriginsFetchAllSuccessAction和IMediasOriginsFetchAllFailedAction在有效负载级别之间似乎存在冲突,这使得其中之一无法分配为MediasAction类型。但这不是| |的全部目的。创建类型时使用运算符?

解决方法

您可以通过在返回操作时将as const添加到MediasActionTypes.*中来解决此问题:

const getAllMediasOriginEpic: Epic<MediasAction,MediasAction> = (action$) => {
  const request = new ApiClient();
  return action$.pipe(
    filter(isOfType(MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL)),switchMap((action) =>
      from(request.getAllOriginMedias()).pipe(
        map((response: IMediasOrigin[]) => ({
          type: MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL_SUCCESS as const,payload: { medias: response }
        })),catchError((error: Error) =>
          of({
            type: MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL_FAILED as const,payload: { error: error.message }
          })
        )
      )
    )
  );
};

这是因为当您使用类似这样的内容时:

map((response: IMediasOrigin[]) => ({
    type: MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL_SUCCESS,payload: { medias: response }
  })),

Observable的类型为:{ type: MediasActionTypes },...。如您所见,您的错误

Type 'MediasActionTypes' is not assignable to type 'MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL_FAILED'

'MediasActionTypes'表示联合,而MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL_FAILED表示具体类型。因此,使用type: MediasActionTypes.MEDIAS_ORIGINS_FETCH_ALL_SUCCESS as const可以缩小类型。