【问题标题】:React + Redux-Observable + Typescript - Compilation, Argument Not Assignable ErrorReact + Redux-Observable + Typescript - 编译,参数不可分配错误
【发布时间】:2019-04-24 05:11:48
【问题描述】:

我正在使用 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&lt;LoginAction&gt;(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 类型的情况下解决此问题?

【问题讨论】:

    标签: reactjs typescript rxjs redux-observable


    【解决方案1】:

    您可以查看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
    

    史诗

    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))),
          ),
        ),
      );
    

    史诗来自redux-observable 库,AllActions 是作为史诗的输入和输出的动作。

    种类如下:

    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);
      })
    

    【讨论】:

      【解决方案2】:

      Typescript 给出了一个错误,因为你设置了ActionsObservable action generic a redux Action,它是一个{ type: string } 形式的接口。所以 Typescript 认为所有即将到来的动作都只有类型,但是你设置为 ofType 过滤运算符是另一种类型的动作,即使它扩展了 redux 动作接口 Typescript 将需要 bivarianceHack 来允许你传递任何实现该接口的东西.最简单的解决方法之一是将ActionsObservable&lt;Action&gt; 更改为ActionsObservable&lt;AnyAction&gt;,使用从redux 导入的AnyAction

      【讨论】:

        【解决方案3】:

        redux-observable 提供的 ofType 运算符并不是区分联合类型的最佳方法。更好的方法是使用typesafe-actions提供的isOfType函数。

        import { filter } from 'rxjs/operators';
        import { isOfType } from 'typesafe-actions';
        

        首先,让我们告诉 TypeScript 您的应用程序中可能使用的操作。您的操作流不应定义为ActionsObservable&lt;Action&gt;,而应定义为您的操作流:ActionsObservable&lt;LoginActions&gt;

        export const loginUserEpic = (action$: ActionsObservable<LoginActions>) =>
        

        现在我们可以将isOfType 谓词与filter 运算符一起使用。替换这个:

        ofType<LoginAction>(LoginActionTypes.LOGIN_ACTION)
        

        用这个:

        filter(isOfType(LoginActionTypes.LOGIN_ACTION))
        

        在流中传递的动作将被正确识别为LoginAction

        【讨论】:

        • 有没有办法在不使用typesafe-actions的情况下解决这个问题?
        • 实现非常简单且可移植。如果您愿意,您可以将其复制粘贴到您的项目中。 github.com/piotrwitek/typesafe-actions/blob/…
        • 我最终实现了typesafe-actions 方法。我真的很喜欢它——我的动作和减速器突然变得“更干净”了。
        • 令人惊讶的是,这在数小时后拯救了我
        猜你喜欢
        • 2020-07-04
        • 2020-07-29
        • 1970-01-01
        • 2018-05-15
        • 2017-04-11
        • 2021-10-17
        • 2020-03-26
        • 2019-02-17
        • 2019-07-01
        相关资源
        最近更新 更多