Extension Types (Inline Classes) în Dart 3.3: Abstractizări Zero-Cost și Tipizare Statică Fără Alocare pe Heap în Dart

Ghid tehnic pentru Extension Types introduse în Dart 3.3: încapsularea tipurilor existente cu cost zero de memorie la runtime, interoperabilitate optimizată cu JavaScript și API-uri sigure pentru ID-uri tipizate.

Sintaxă
extension type UserId(int id) { bool get isValid => id > 0; }

În proiectarea de sisteme robuste, utilizarea tipurilor primitive (cum ar fi int pentru identificatori de utilizatori, comenzi și produse) duce frecvent la bug-uri subtile: transmiterea accidentală a unui orderId în locul unui userId nu este semnalată de compilator dacă ambele sunt simple numere întregi.

Soluția clasică – crearea unei clase wrapper class UserId { final int value; ... } – aduce o penalizare severă de performanță: fiecare ID alocă un obiect nou pe heap, crește presiunea pe Garbage Collector și afectează randamentul în bucle critice.

Lansate ca facilitate stabilă în Dart 3.3, Extension Types (cunoscute și ca inline classes sau zero-cost abstractions) rezolvă această dilemă: oferă tipizare statică strictă la compilare, dar la execuție sunt complet eliminate, binarul executând codul cu tipul primitiv original, cu zero alocare de memorie suplimentară!

Definirea și Utilizarea unui Extension Type
// Definirea a două tipuri distincte peste tipul primitiv int:
extension type const UserId(int id) {
  // Putem adăuga metode și proprietăți calculate fără cost de instanțiere:
  bool get isValid => id > 0;
  void display() => print('ID Utilizator: $id');
}

extension type const OrderId(int id) {}

void cancelOrder(UserId user, OrderId order) {
  print('Anulare comanda ${order.id} pentru utilizatorul ${user.id}');
}

void main() {
  final user = UserId(1001);
  final order = OrderId(5002);

  // Apel valid tipizat:
  cancelOrder(user, order);

  // ❌ EROARE DE COMPILARE: Inversarea argumentelor este detectată imediat!
  // cancelOrder(order, user);
  // Error: The argument type 'OrderId' can't be assigned to the parameter type 'UserId'.

  // La runtime, user și order sunt simple 'int' pe 64 de biți pe stivă!
}

Tabel Comparativ: Extension Type vs Extension Method vs Clasă Tradițională

Caracteristică Extension Type (Dart 3.3+) Extension Methods (Dart 2.7+) Clasă Wrapper Tradițională
Alocare pe Heap 0 bytes (Zero-Cost) 0 bytes Da (Obiect complet pe heap)
Tip Nou la Compilare Da (Incompatibil cu tipul de bază fără cast) Nu (Tipul rămâne cel original) Da
Ascunderea Interfeței Bazei Da (Interfața originală nu este expusă implicit) Nu (Toate metodele originale rămân accesibile) Da
Interoperabilitate JS / Web Nativă (folosit masiv în dart:js_interop) Limitată Necesită serializare / wrapping
Redenumirea sau Expunerea Selectivă a Interfeței de Bază
// Folosind 'implements', putem alege să păstrăm metodele tipului de bază:
extension type SafeString(String _raw) implements String {
  // Datorită 'implements String', avem acces direct la .length, .contains etc.,
  // dar putem suprascrie sau adăuga validări personalizate:
  bool get isSanitized => !contains('<script>');
}

void main() {
  final text = SafeString('Salutare lume');
  print(text.length); // 13 (din interfața String)
  print(text.isSanitized); // true
}