# Generics, Extension Methods and Extension Types — Dart

Source: https://www.skillbyai.com/en/dart/m-generics

> Write generic code and extend existing types safely.

## Reusable and extensible code

**Generics** let classes and functions work with any type while keeping type safety: `class Cache<K, V>`, `T firstOr<T>(List<T> items, T fallback)`. Bound type parameters restrict them: `T extends Comparable<T>`. Dart generics are **reified**, so the runtime knows `List<int>` from `List<String>` and `is List<int>` checks work. **Extension methods** add methods, getters and operators to existing types you do not control, such as adding `toRupees()` to `int` or `capitalised` to `String`; they are resolved statically by the variable's type and must be imported to be visible. **Extension types** (Dart 3.3) wrap an existing type in a new static type with zero run-time cost: `extension type UserId(String value) {}` prevents accidentally passing an email string where a user ID is expected, while compiling down to the underlying `String`. They are useful for IDs, units and safer interop wrappers, but because the wrapper disappears at run time, they are not a security boundary: a cast can bypass them.

## Generics, an extension and an extension type

Type-safe reuse and zero-cost wrappers.

```dart
class Cache<K, V> {
  final _store = <K, (V, DateTime)>{};
  final Duration ttl;
  Cache(this.ttl);

  V? get(K key) {
    final entry = _store[key];
    if (entry == null) return null;
    final (value, storedAt) = entry;
    return DateTime.now().difference(storedAt) < ttl ? value : null;
  }

  void put(K key, V value) => _store[key] = (value, DateTime.now());
}

T maxOf<T extends Comparable<T>>(List<T> items) =>
    items.reduce((a, b) => a.compareTo(b) >= 0 ? a : b);

extension RupeeFormat on int {
  String get asRupees => 'Rs ${(this / 100).toStringAsFixed(2)}';   // int paise -> text
}

extension type OrderId(String value) {
  OrderId.parse(String raw) : value = raw.trim().toUpperCase();
  bool get isValid => value.startsWith('ORD-');
}

void cancel(OrderId id) => print('Cancelling ${id.value}');

void main() {
  final prices = Cache<String, int>(const Duration(minutes: 5))..put('pen', 4950);
  print(prices.get('pen')?.asRupees);   // Rs 49.50
  print(maxOf([3, 9, 4]));               // 9
  cancel(OrderId.parse(' ord-42 '));
  // cancel('ORD-42');                   // compile error: String is not an OrderId
}
```

## Extensions must be imported

Extension methods are only available where their library is imported. If a method seems to vanish in another file, check the import before suspecting the compiler.

**Quiz:** What is the main benefit of an extension type such as OrderId(String value)?

- [ ] It adds run-time validation automatically
- [ ] It makes the value mutable
- [x] It gives a distinct static type with no run-time overhead, preventing mix-ups with plain strings
- [ ] It replaces generics

*Answer:* It gives a distinct static type with no run-time overhead, preventing mix-ups with plain strings. Extension types are compile-time wrappers that erase to the underlying type at run time.
