简体   繁体   中英

How to properly type fp-ts ObservableEither#fold?

I'm attempting to use fp-ts and redux-observable to build some epics that process some api requests. I am having an issue with fp-ts-rxjs/ObservableEither#fold where if I do not cast my actions into type AnyAction I get a type error saying that the two types are expected to be the same.

Type 'Observable<{ payload: { user: User<Attributes>; }; type: string; }>' is not assignable to type 'Observable<{ payload: { error: Error | null; }; type: string; }>'.
  Type '{ payload: { user: User<Attributes>; }; type: string; }' is not assignable to type '{ payload: { error: Error | null; }; type: string; }'.
    Types of property 'payload' are incompatible.
      Property 'error' is missing in type '{ user: User<Attributes>; }' but required in type '{ error: Error | null; }'

I have also tried using fp-ts-rxjs/ObservableEither#bimap because it expects two different types to be returned. However, this causes a runtime error saying that an action cannot have an undefined type. I'm not sure exactly what is going on there too.

LoginSlice.ts

const loginSlice = createSlice({
  name: 'login',
  initialState,
  reducers: {
    loginSuccess (state, action: PayloadAction<{ user: User }>) {
      state.loggedIn = true;
    },

    loginReset (state) {
      state.error = null;
    },

    loginFail (state, action: PayloadAction<{ error: Error | null } >) {
      state.error = action.payload.error;
    }
  }
});

LoginService.ts

const loginService = (credentials: LoginInfo): OE.ObservableEither<Error, User> => {
  const { username, password } = credentials;

  return OE.fromTaskEither(
    TE.tryCatch(
      async () => await User.logIn(username, password),
      error => error as Error
    )
  );
};

LoginEpics.ts

export const loginEpic: Epic = (action$: ActionsObservable<AnyAction>) => action$.pipe(
  filter(login.match),
  mergeMap((action) =>
    loginService(action.payload.credentials).pipe(
      fold(
        (error) => of(loginFail({ error }) as AnyAction),
        (user) => of(loginSuccess({ user }) as AnyAction)
      )
    )
  )
);

Is there a way to avoid casting actions into AnyAction ? Any insight would be greatly appreciated.

I am not familiar with fp-ts-rxjs/ObservableEither , but if their fold function is defined similarly to how regular fp-ts fold functions are defined, I think your problem can be resolved by specifying an exact return type from one or both of the anonymous functions you pass into fold .

When no return type is specified, TypeScript tries to infer the return type from the first function and is not clever enough to realize that because the two anonymous functions return different types, the fold result should be the union of the two return types.

Assuming you have LoginFailure and LoginSuccess action types, a fold without AnyAction would look like:

fold(
  (error): LoginFailure | LoginSuccess => of(loginFail({ error })),
  (user): LoginFailure | LoginSuccess => of(loginSuccess({ user }))
)

Here we specify that the return type should be the union of the possible returned actions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM