dartsealed-class

With the new Dart sealed-classes feature, is it possible to do a nested switch?


I have a sealed class for FailureOrSuccess, with subclasses for Success, and Failure.ch

The Failure subclass is also a sealed class, with multiple subclasses for the different types of failure.

I would like to have a switch statement that switches between failure and success, and within the failure code block, have another switch statement that allows me to switch between the different failure options?

Is it possible to do this with nested sealed-classes?

(here's an example of what I'm trying to do below, though this doesn't work).

switch (state.authFailureOrSuccessOption) {
          case Success(): {
            context.router.replace(const DailyRoute());
          }
          case Failure(): {
            String text;

            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text(switch (???) {
                  case CancelledByUser:
                    return 'Cancelled';
                  case ServerError:
                    return 'Server Error';
                  case EmailAlreadyInUse:
                    return 'Email already in use';
                  case InvalidEmailAndPasswordCombination:
                    return 'Invalid email and password combination';
                  case UnknownError:
                    return 'An unknown error occurred';
                }),));
          }
        }

And here's a list of my sealed-classes for reference:

// Overall Class
sealed class FailureOrSuccess {
  const FailureOrSuccess();
}

    // Level 2 Subclass
    class Success extends FailureOrSuccess {
      const Success();
    }

    sealed class Failure extends FailureOrSuccess {
      const Failure();
    }

        // Level 3 Subclass
        sealed class AuthFailure extends Failure {
          const AuthFailure();
        }

            // Leven 4 Subclasses of AuthFailure
            class CancelledByUser extends AuthFailure {
              const CancelledByUser();
            }

            class ServerError extends AuthFailure {
              const ServerError();
            }

            class EmailAlreadyInUse extends AuthFailure {
              const EmailAlreadyInUse();
            }

            class InvalidEmailAndPasswordCombination extends AuthFailure {
              const InvalidEmailAndPasswordCombination();
            }

            class UnknownError extends AuthFailure {
              const UnknownError();
            }

Solution

  • Yes.

    The exhaustiveness-checking algorithm isn't completely implemented yet, but it's intended that class hierarchies like the above will work.

    You need to write the case patterns differently, though.

    Use of:

      case CancelledByUser _:
      // or
      case CancelledByUser():
    

    The case CancelledByUser: is actually comparing the switch value to the Type object for the type CancelledByUser (which will never succeed, because the CancelledByUser object is not a Type object and not equal to one), it's not actually doing a type check. The case CancelledByUser _: or case CancelledByUser(): patterns do type checks.

    You can nest switches, using the promoted/checked type of the outer match as the match value, and you can use a switch expression in an expression position:

     
              case Failure fail: {
                String text;
    
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(
                    content: Text(switch (fail) {
                      case CancelledByUser() => 'Cancelled',
                      case ServerError => 'Server Error',
                      // ...
                    })