extension type în Dart

Un tip nou, verificat la compilare, care la rulare este doar o valoare existentă (de ex. un int), fără cost.

Sintaxă
extension type Nume(TipReprezentare valoare) {
  // metode, getter-e, constructori
}
extension type Nume(int v) implements int { ... }   // păstrează și membrii lui int

Ce face extension type

extension type IdClient(int valoare) {} creează un tip separat pentru analizor: un IdClient nu se poate folosi unde se cere un int sau un IdProdus, deci nu le mai poți încurca. La rulare însă nu există niciun obiect nou: valoarea este exact int-ul dinăuntru, fără memorie sau timp în plus.

Membrii tipului reprezentat nu sunt disponibili automat. Adaugi doar metodele pe care le vrei, sau scrii implements int ca să-i păstrezi pe toți. Valoarea interioară se citește prin numele dat în paranteze (id.valoare).

Pentru că la rulare tipul „dispare”, is și as se uită la tipul reprezentat: IdClient(5) is int este true. Extension types sunt folosite mult pentru interoperabilitatea cu JavaScript (dart:js_interop).

Exemple

Exemplul 1: Identificatori care nu se pot încurca

Ambele tipuri sunt int la rulare, dar afiseazaClient(IdProdus(7)) nu ar trece de analizor. runtimeType arată că la rulare valoarea este un simplu int.

extension type IdClient(int valoare) {}

extension type IdProdus(int valoare) {}

void afiseazaClient(IdClient id) => print('Clientul #${id.valoare}');

void main() {
  final client = IdClient(1024);
  final produs = IdProdus(7);
  afiseazaClient(client);
  print('Produsul #${produs.valoare}');
  print(client.runtimeType);
}

Rezultat

Clientul #1024
Produsul #7
int

Exemplul 2: Sume de bani cu operații proprii

Bani are doar operațiile alese; implements nu este folosit, deci Bani nu are restul metodelor lui int.

extension type Bani(int bani) {
  Bani.lei(double lei) : bani = (lei * 100).round();

  Bani operator +(Bani alta) => Bani(bani + alta.bani);
  String get afisare => '${(bani / 100).toStringAsFixed(2)} lei';
}

void main() {
  final total = Bani.lei(19.99) + Bani.lei(5.5) + Bani(1);
  print(total.afisare);
  print(total.bani);
}

Rezultat

25.50 lei
2550

Exemplul 3: implements: păstrarea membrilor tipului de bază

Cu implements int, Varsta poate fi folosită ca un int, dar are și getter-ul propriu.

extension type Varsta(int ani) implements int {
  bool get esteMajor => ani >= 18;
}

void main() {
  final v = Varsta(17);
  print(v.esteMajor);
  print(v + 1);
  int numar = v;
  print(numar.isOdd);
}

Rezultat

false
18
true

Greșeli frecvente

  • Să folosești extension types pentru siguranță la rulare: (x as Object) is IdClient este true pentru orice int, pentru că tipul nu mai există la rulare.
  • Atribuirea unui extension type unei variabile de tipul reprezentat fără implements: A value of type 'Bani' can't be assigned to a variable of type 'int'.
  • Să-l confunzi cu extension: extension adaugă metode tipului existent, extension type creează un tip nou.

Comenzi înrudite

  • extension: Adaugă metode, getter-e și operatori unui tip existent (chiar și String sau int), fără să-l modifici.
  • typedef: Dă un nume (alias) unui tip, de obicei unui tip de funcție sau unui tip generic lung.
  • class: Definește un tip nou de obiect, cu câmpuri (date) și metode (comportament).
  • external: Declară o funcție sau un câmp al cărui corp este scris în altă parte: în C (dart:ffi), JavaScript sau în SDK.