React + Redux-Observable + Typescript - 编译,参数不可分配错误
React + Redux-Observable + Typescript - Compilation, Argument Not Assignable Error
我正在使用 React 和 Redux-Observable 创建一个应用程序。我是新手,我正在尝试创建史诗来执行用户登录。
我的史诗如下:
export const loginUserEpic = (action$: ActionsObservable<Action>) =>
action$.pipe(
ofType<LoginAction>(LoginActionTypes.LOGIN_ACTION),
switchMap((action: LoginAction) =>
ajax({
url,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: { email: action.payload.username, password: action.payload.password },
}).pipe(
map((response: AjaxResponse) => loginSuccess(response.response.token)),
catchError((error: Error) => of(loginFailed(error))),
),
),
);
问题是我在这一行收到 Typescript 错误:ofType<LoginAction>(LoginActionTypes.LOGIN_ACTION)
说:
Argument of type '(source: Observable<LoginAction>) => Observable<LoginAction>' is not assignable to parameter of type 'OperatorFunction<Action<any>, LoginAction>'.
Types of parameters 'source' and 'source' are incompatible.
Type 'Observable<Action<any>>' is not assignable to type 'Observable<LoginAction>'.
Type 'Action<any>' is not assignable to type 'LoginAction'.
Property 'payload' is missing in type 'Action<any>'.
我的操作在这里:
export enum LoginActionTypes {
LOGIN_ACTION = 'login',
LOGIN_SUCCESS_ACTION = 'login-sucesss',
LOGIN_FAILED_ACTION = 'login-failed',
}
export interface LoginAction extends Action {
type: LoginActionTypes.LOGIN_ACTION;
payload: {
username: string;
password: string;
};
}
export function login(username: string, password: string): LoginAction {
return {
type: LoginActionTypes.LOGIN_ACTION,
payload: { username, password },
};
}
export interface LoginSuccessAction extends Action {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION;
payload: {
loginToken: string;
};
}
export function loginSuccess(loginToken: string): LoginSuccessAction {
return {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION,
payload: { loginToken },
};
}
export interface LoginFailedAction extends Action {
type: LoginActionTypes.LOGIN_FAILED_ACTION;
payload: {
error: Error;
};
}
export function loginFailed(error: Error): LoginFailedAction {
return {
type: LoginActionTypes.LOGIN_FAILED_ACTION,
payload: { error },
};
}
export type LoginActions = LoginAction | LoginSuccessAction | LoginFailedAction;
如何在不使用 Epic 上的 any
类型的情况下解决此问题?
redux-observable
提供的 ofType
运算符不是区分联合类型的最佳方式。更好的方法是使用 typesafe-actions
.
提供的 isOfType
函数
import { filter } from 'rxjs/operators';
import { isOfType } from 'typesafe-actions';
首先,让我们告诉 TypeScript 您的应用程序中可能使用的操作。您的操作流不应定义为 ActionsObservable<Action>
,而应定义为 您的 操作流:ActionsObservable<LoginActions>
.
export const loginUserEpic = (action$: ActionsObservable<LoginActions>) =>
现在我们可以将 isOfType
谓词与 filter
运算符一起使用。替换为:
ofType<LoginAction>(LoginActionTypes.LOGIN_ACTION)
有了这个:
filter(isOfType(LoginActionTypes.LOGIN_ACTION))
向下传递的动作将被正确识别为 LoginAction
。
您可以查看 https://github.com/piotrwitek/react-redux-typescript-guide 下面的详细信息,了解使用 React、Redux 和 Redux-Observable 时的所有标准操作方式。
我建议使用 typesafe-actions
库来实现类型。
一些伪代码:
操作数
而不是这个
export interface LoginSuccessAction extends Action {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION;
payload: {
loginToken: string;
};
}
export function loginSuccess(loginToken: string): LoginSuccessAction {
return {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION,
payload: { loginToken },
};
}
使用typesafe-actions
,无界面
actions/login/LoginActions.ts
import { action } from "typesafe-actions"
export function loginSuccess(loginToken: string) {
return action(LoginActionTypes.LOGIN_SUCCESS_ACTION, { loginToken });
}
actions/login/LoginActionsModel.ts
import * as LoginActions from "./LoginActions";
import { ActionCreator } from "typesafe-actions";
export type LoginActionCreator = ActionCreator<typeof LoginActions>
然后导出所有动作。
actions/index.ts
import { LoginActionCreator } from "./login/LoginActionModel";
export default type AllActions = LoginActionCreator
Epics
import { Epic } from "redux-observable";
import { isOfType } from "typesafe-actions";
import { filter } from "rxjs/operators";
export const loginUserEpic: Epic<AllActions> = (action$) =>
action$.pipe(
filter(isOfType((LoginActionTypes.LOGIN_ACTION))),
switchMap((action: LoginAction) =>
ajax({
url,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: { email: action.payload.username, password: action.payload.password },
}).pipe(
map((response: AjaxResponse) => loginSuccess(response.response.token)),
catchError((error: Error) => of(loginFailed(error))),
),
),
);
其中 Epics 来自 redux-observable
库,AllActions 是 epics.
的输入和输出动作
类型如下:
Epic<InputActions, OutputActions, Store>
Epic<Actions(Input&Output)>
如果你想使用 redux 的 store,你需要一个 RootState(来自 root reducer)
export const someEpic: Epic<AllActions, AllActions, RootState> = (action$, store$)
=> action$.pipe(
filter(isOfType(SOMETYPE)),
mergeMap(action => {
const a = store$.value.a;
return someActions(a, action.payload.b);
})
Typescript 给出了一个错误,因为你已经设置了 ActionsObservable
action generic a redux Action
,它是一个具有 { type: string }
形式的接口。所以 Typescript 认为所有即将到来的动作都只有类型,但是你设置为 ofType
过滤运算符另一种类型的 Action,即使它扩展了 redux Action 接口 Typescript 也需要 bivarianceHack
来允许你传递任何东西实现该接口。最简单的解决方法之一是将 ActionsObservable<Action>
更改为 ActionsObservable<AnyAction>
,并使用从 redux 导入的 AnyAction
。
我正在使用 React 和 Redux-Observable 创建一个应用程序。我是新手,我正在尝试创建史诗来执行用户登录。
我的史诗如下:
export const loginUserEpic = (action$: ActionsObservable<Action>) =>
action$.pipe(
ofType<LoginAction>(LoginActionTypes.LOGIN_ACTION),
switchMap((action: LoginAction) =>
ajax({
url,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: { email: action.payload.username, password: action.payload.password },
}).pipe(
map((response: AjaxResponse) => loginSuccess(response.response.token)),
catchError((error: Error) => of(loginFailed(error))),
),
),
);
问题是我在这一行收到 Typescript 错误:ofType<LoginAction>(LoginActionTypes.LOGIN_ACTION)
说:
Argument of type '(source: Observable<LoginAction>) => Observable<LoginAction>' is not assignable to parameter of type 'OperatorFunction<Action<any>, LoginAction>'.
Types of parameters 'source' and 'source' are incompatible.
Type 'Observable<Action<any>>' is not assignable to type 'Observable<LoginAction>'.
Type 'Action<any>' is not assignable to type 'LoginAction'.
Property 'payload' is missing in type 'Action<any>'.
我的操作在这里:
export enum LoginActionTypes {
LOGIN_ACTION = 'login',
LOGIN_SUCCESS_ACTION = 'login-sucesss',
LOGIN_FAILED_ACTION = 'login-failed',
}
export interface LoginAction extends Action {
type: LoginActionTypes.LOGIN_ACTION;
payload: {
username: string;
password: string;
};
}
export function login(username: string, password: string): LoginAction {
return {
type: LoginActionTypes.LOGIN_ACTION,
payload: { username, password },
};
}
export interface LoginSuccessAction extends Action {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION;
payload: {
loginToken: string;
};
}
export function loginSuccess(loginToken: string): LoginSuccessAction {
return {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION,
payload: { loginToken },
};
}
export interface LoginFailedAction extends Action {
type: LoginActionTypes.LOGIN_FAILED_ACTION;
payload: {
error: Error;
};
}
export function loginFailed(error: Error): LoginFailedAction {
return {
type: LoginActionTypes.LOGIN_FAILED_ACTION,
payload: { error },
};
}
export type LoginActions = LoginAction | LoginSuccessAction | LoginFailedAction;
如何在不使用 Epic 上的 any
类型的情况下解决此问题?
redux-observable
提供的 ofType
运算符不是区分联合类型的最佳方式。更好的方法是使用 typesafe-actions
.
isOfType
函数
import { filter } from 'rxjs/operators';
import { isOfType } from 'typesafe-actions';
首先,让我们告诉 TypeScript 您的应用程序中可能使用的操作。您的操作流不应定义为 ActionsObservable<Action>
,而应定义为 您的 操作流:ActionsObservable<LoginActions>
.
export const loginUserEpic = (action$: ActionsObservable<LoginActions>) =>
现在我们可以将 isOfType
谓词与 filter
运算符一起使用。替换为:
ofType<LoginAction>(LoginActionTypes.LOGIN_ACTION)
有了这个:
filter(isOfType(LoginActionTypes.LOGIN_ACTION))
向下传递的动作将被正确识别为 LoginAction
。
您可以查看 https://github.com/piotrwitek/react-redux-typescript-guide 下面的详细信息,了解使用 React、Redux 和 Redux-Observable 时的所有标准操作方式。
我建议使用 typesafe-actions
库来实现类型。
一些伪代码:
操作数
而不是这个
export interface LoginSuccessAction extends Action {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION;
payload: {
loginToken: string;
};
}
export function loginSuccess(loginToken: string): LoginSuccessAction {
return {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION,
payload: { loginToken },
};
}
使用typesafe-actions
,无界面
actions/login/LoginActions.ts
import { action } from "typesafe-actions"
export function loginSuccess(loginToken: string) {
return action(LoginActionTypes.LOGIN_SUCCESS_ACTION, { loginToken });
}
actions/login/LoginActionsModel.ts
import * as LoginActions from "./LoginActions";
import { ActionCreator } from "typesafe-actions";
export type LoginActionCreator = ActionCreator<typeof LoginActions>
然后导出所有动作。
actions/index.ts
import { LoginActionCreator } from "./login/LoginActionModel";
export default type AllActions = LoginActionCreator
Epics
import { Epic } from "redux-observable";
import { isOfType } from "typesafe-actions";
import { filter } from "rxjs/operators";
export const loginUserEpic: Epic<AllActions> = (action$) =>
action$.pipe(
filter(isOfType((LoginActionTypes.LOGIN_ACTION))),
switchMap((action: LoginAction) =>
ajax({
url,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: { email: action.payload.username, password: action.payload.password },
}).pipe(
map((response: AjaxResponse) => loginSuccess(response.response.token)),
catchError((error: Error) => of(loginFailed(error))),
),
),
);
其中 Epics 来自 redux-observable
库,AllActions 是 epics.
类型如下:
Epic<InputActions, OutputActions, Store>
Epic<Actions(Input&Output)>
如果你想使用 redux 的 store,你需要一个 RootState(来自 root reducer)
export const someEpic: Epic<AllActions, AllActions, RootState> = (action$, store$)
=> action$.pipe(
filter(isOfType(SOMETYPE)),
mergeMap(action => {
const a = store$.value.a;
return someActions(a, action.payload.b);
})
Typescript 给出了一个错误,因为你已经设置了 ActionsObservable
action generic a redux Action
,它是一个具有 { type: string }
形式的接口。所以 Typescript 认为所有即将到来的动作都只有类型,但是你设置为 ofType
过滤运算符另一种类型的 Action,即使它扩展了 redux Action 接口 Typescript 也需要 bivarianceHack
来允许你传递任何东西实现该接口。最简单的解决方法之一是将 ActionsObservable<Action>
更改为 ActionsObservable<AnyAction>
,并使用从 redux 导入的 AnyAction
。