如何使用 TypeScript 为 Redux-Observable 的 Epics 键入操作
How to type Actions for Redux-Observable's Epics with TypeScript
我正在我的应用程序中设置 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 爱好者来说,这个错误可能是显而易见的,但根据 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-observable: 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, 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
可以缩小类型。
我正在我的应用程序中设置 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 爱好者来说,这个错误可能是显而易见的,但根据 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-observable: 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, 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
可以缩小类型。