brianegan / dart_redux_epics

Redux.dart middleware for handling actions using Dart Streams
MIT License
141 stars 22 forks source link

Unhandled Exception: type 'SetEventItemFailAction' is not a subtype of type 'FutureOr<UpdateTaskAction>' #38

Closed aliyoge closed 4 years ago

aliyoge commented 4 years ago

Here is my code:

Stream<dynamic> setEventItemsEffect(
    Stream<dynamic> actions, EpicStore<AppState> store) {
  return actions
      .whereType<SetEventItemAction>()
      .asyncMap((action) => handleEventItems(action.task)
        .then((res) => UpdateTaskAction(task: action.task)
        .catchError((_) => SetEventItemFailAction(task: action.task))
  );
}

When to catchError, this code get error Unhandled Exception: type 'SetEventItemFailAction' is not a subtype of type 'FutureOr<UpdateTaskAction>'

When I change code to this according to #30 :

Stream<dynamic> setEventItemsEffect(
    Stream<dynamic> actions, EpicStore<AppState> store) {
  return actions
      .whereType<SetEventItemAction>()
      .asyncMap((action) => Stream<dynamic>.fromFuture(handleEventItems(action.task)
        .then((res) => UpdateTaskAction(task: action.task)
        .catchError((_) => SetEventItemFailAction(task: action.task)))
  );
}

The error is gone, but the type goes like this. Action: Instance of '_ControllerStream<dynamic>'

or:

Stream<dynamic> setEventItemsEffect(
    Stream<dynamic> actions, EpicStore<AppState> store) {
  return actions
      .whereType<SetEventItemAction>()
      .asyncMap((action) => Stream<UpdateTaskAction>.fromFuture(handleEventItems(action.task)
        .then((res) => UpdateTaskAction(task: action.task)
        .catchError((_) => SetEventItemFailAction(task: action.task)))
  );
}

The type goes like this. Action: Instance of '_ControllerStream<dynamic>'

aliyoge commented 4 years ago

Update:

I solved this problem in this way:

Stream<dynamic> setEventItemsEffect(
    Stream<dynamic> actions, EpicStore<AppState> store) {
  return actions
      .whereType<SetEventItemAction>()
      .asyncMap((action) => handleEventItems(action.task)
        .then<dynamic>((res) => UpdateTaskAction(task: action.task)
        .catchError((_) => SetEventItemFailAction(task: action.task))
  );
}

Is there a better way? @brianegan

brianegan commented 4 years ago

Hey there -- Futures can only return one type of Object. In your original example, Dart is inferring that you want to create a Future<UpdateTaskAction>, but if an error occurs you're trying to return a different type: SetEventItemFailAction. The future cannot do that -- it can only return one type!

Therefore, you have two options:

  1. Make each of these action classes implement a common parent interface
  2. Use Object or dynamic as a default parent interface -- which is what Redux does, and what you've done!

Therefore, the way you've solved it is completely appropriate, but hope this explanation helps you understand why :)

aliyoge commented 4 years ago

Hey there -- Futures can only return one type of Object. In your original example, Dart is inferring that you want to create a Future<UpdateTaskAction>, but if an error occurs you're trying to return a different type: SetEventItemFailAction. The future cannot do that -- it can only return one type!

Therefore, you have two options:

  1. Make each of these action classes implement a common parent interface
  2. Use Object or dynamic as a default parent interface -- which is what Redux does, and what you've done!

Therefore, the way you've solved it is completely appropriate, but hope this explanation helps you understand why :)

Thank you very much for your explanation.