# Enhanced Enums and Dart 3 Class Modifiers — Dart

Source: https://www.skillbyai.com/en/dart/c-modifiers

> Use enhanced enums and the sealed, final, base and interface modifiers.

## Controlling how types can be used

**Enhanced enums** (Dart 2.17+) can have fields, constructors, methods and implement interfaces: `enum OrderStatus { placed('Placed'), paid('Paid'); const OrderStatus(this.label); final String label; }`. Enums also give you `values`, `name` and `byName`. **Dart 3 class modifiers** let library authors control how classes are used outside their library. **`sealed`** declares a closed family: subclasses must be in the same library, so a `switch` over a sealed type can be checked for **exhaustiveness**, which is perfect for modelling results and states. **`final`** prevents extending and implementing outside the library. **`base`** allows extending but not implementing outside the library, guaranteeing implementation inheritance. **`interface`** allows implementing but not extending outside the library. **`mixin class`** declares something usable as both a mixin and a class. These modifiers matter most for package authors, but `sealed` is useful in every app for modelling state.

## An enhanced enum and a sealed result type

Exhaustive switching over sealed subclasses.

```dart
enum OrderStatus {
  placed('Placed'),
  paid('Paid'),
  shipped('On the way'),
  cancelled('Cancelled');

  const OrderStatus(this.label);
  final String label;

  bool get isFinal => this == shipped || this == cancelled;
}

sealed class PaymentResult {}

class PaymentSuccess extends PaymentResult {
  PaymentSuccess(this.paymentId);
  final String paymentId;
}

class PaymentDeclined extends PaymentResult {
  PaymentDeclined(this.reason);
  final String reason;
}

class PaymentPending extends PaymentResult {}

String describe(PaymentResult r) => switch (r) {      // exhaustive: compiler checks all subtypes
      PaymentSuccess(:final paymentId) => 'Paid ($paymentId)',
      PaymentDeclined(:final reason) => 'Declined: $reason',
      PaymentPending() => 'Waiting for confirmation',
    };

void main() {
  print(OrderStatus.paid.label);                   // Paid
  print(OrderStatus.values.byName('shipped').isFinal);   // true
  print(describe(PaymentDeclined('Insufficient funds')));
}
```

## Sealed types make new cases loud

Adding a new subclass to a sealed family turns every non-exhaustive switch into a compile error, pointing you to each place that must handle the new case.

**Quiz:** What does marking a class sealed enable?

- [x] Exhaustiveness checking in switch, because all subtypes are known within the library
- [ ] Faster method calls
- [ ] Multiple inheritance
- [ ] Automatic JSON serialisation

*Answer:* Exhaustiveness checking in switch, because all subtypes are known within the library. Sealed classes have a known, closed set of subtypes.
