Fonctionnalités Modernes de Dart
1. Quand utiliser cette compétence
Utilisez cette compétence quand :
- Vous écrivez ou vérifiez du code Dart ciblant Dart 3.0 ou une version ultérieure.
- Vous refactorisez du code Dart legacy pour utiliser des fonctionnalités modernes, concises et sûres.
- Vous recherchez des moyens idiomatiques de gérer plusieurs valeurs de retour, l'extraction de données imbriquées ou la vérification exhaustive.
Découverte
Pour trouver des candidats à la modernisation :
Switch Expressions
Recherchez les déclarations switch où chaque case assigne à la même variable ou retourne :
- Regex :
switch\s*\([^)]+\)\s*\{\s*case
Candidats Pattern Matching
Recherchez l'extraction manuelle de propriétés de map ou JSON et la vérification de type :
- Regex :
containsKey\(['"][^'"]+['"]\) - Regex :
json\[['"][^'"]+['"]\]\s+is\s+
Éléments Null-Aware
Recherchez les déclarations if de collection vérifiant la nullité :
- Regex :
if\s*\(\w+\s*!=\s*null\)\s*\w+
Séparateurs de Chiffres
Recherchez les longs nombres sans séparateurs :
- Regex :
\b\d{6,}\b(Correspond aux nombres ayant 6 chiffres ou plus).
2. Fonctionnalités
Records
Utilisez les records comme des structures agrégées anonymes et immuables pour regrouper plusieurs objets sans définir de classe personnalisée. Préférez-les pour retourner plusieurs valeurs d'une fonction ou regrouper temporairement des données connexes.
À éviter : Créer une classe dédiée pour des retours simples à plusieurs valeurs.
class UserResult {
final String name;
final int age;
UserResult(this.name, this.age);
}
UserResult fetchUser() {
return UserResult('Alice', 42);
}
À préférer : Utiliser les records pour regrouper les types sans friction à la volée.
(String, int) fetchUser() {
return ('Alice', 42);
}
void main() {
var user = fetchUser();
print(user.$1); // Alice
}
Patterns et Pattern Matching
Utilisez les patterns pour déstructurer les données complexes en variables locales et comparer contre des formes ou des valeurs spécifiques. Utilisez-les dans switch, if-case, ou les déclarations de variable pour déplier directement les données.
À éviter : Vérifier manuellement les types, les nulls et les clés pour l'extraction de données.
void processJson(Map<String, dynamic> json) {
if (json.containsKey('name') && json['name'] is String &&
json.containsKey('age') && json['age'] is int) {
String name = json['name'];
int age = json['age'];
print('$name is $age years old.');
}
}
À préférer : Combiner la vérification de type, la validation et l'assignation en une seule déclaration.
void processJson(Map<String, dynamic> json) {
if (json case {'name': String name, 'age': int age}) {
print('$name is $age years old.');
}
}
Switch Expressions
Utilisez les switch expressions pour retourner une valeur directement, éliminant les déclarations case et break encombrantes.
À éviter : Utiliser les déclarations switch où chaque branche retourne ou assigne simplement une valeur.
String describeStatus(int code) {
switch (code) {
case 200:
return 'Success';
case 404:
return 'Not Found';
default:
return 'Unknown';
}
}
À préférer :
Retourner l'expression évaluée directement en utilisant la syntaxe =>.
String describeStatus(int code) => switch (code) {
200 => 'Success',
404 => 'Not Found',
_ => 'Unknown',
};
Class Modifiers
Utilisez les modificateurs de classe (sealed, final, base, interface) pour restreindre comment les classes peuvent être utilisées en dehors de leur bibliothèque définie. Préférez sealed pour définir des familles fermées de sous-types pour activer la vérification exhaustive.
À éviter :
Utiliser des classes abstract ouvertes quand l'ensemble des sous-classes est connu et fixe.
abstract class Result {}
class Success extends Result {}
class Failure extends Result {}
String handle(Result r) {
if (r is Success) return 'OK';
if (r is Failure) return 'Error';
return 'Unknown';
}
À préférer :
Utiliser sealed pour garantir au compilateur que tous les cas sont couverts.
sealed class Result {}
class Success extends Result {}
class Failure extends Result {}
String handle(Result r) => switch(r) {
Success() => 'OK',
Failure() => 'Error',
};
Extension Types
Utilisez les extension types pour un wrapper à coût zéro autour d'un type existant. Utilisez-les pour restreindre les opérations ou ajouter un comportement personnalisé sans surcharge d'exécution.
À éviter : Allouer de nouveaux objets wrapper juste pour la logique spécifique au domaine ou la sécurité de type.
class Id {
final int value;
Id(this.value);
bool get isValid => value > 0;
}
À préférer : Utiliser les extension types qui se compilent jusqu'au type sous-jacent à l'exécution.
extension type Id(int value) {
bool get isValid => value > 0;
}
Digit Separators
Utilisez les underscores (_) dans les littéraux numériques strictement pour améliorer la lisibilité visuelle des grandes valeurs numériques.
À éviter : Les longs littéraux numériques qui sont difficiles à lire en un coup d'œil.
const int oneMillion = 1000000;
À préférer : Utiliser les underscores pour séparer les milliers ou d'autres groupements.
const int oneMillion = 1_000_000;
Wildcard Variables
Utilisez les wildcards (_) comme variables non-liantes ou paramètres pour signaler explicitement qu'une valeur est intentionnellement inutilisée.
À éviter : Inventer des noms de variable clunky et distincts pour éviter les avertissements "unused variable".
void handleEvent(String ignoredName, int status) {
print('Status: $status');
}
À préférer : Explicitement abandonner la liaison avec un underscore.
void handleEvent(String _, int status) {
print('Status: $status');
}
Null-Aware Elements
Utilisez les éléments null-aware (?) dans les littéraux de collection pour inclure conditionnellement des éléments seulement s'ils évaluent à une valeur non-null.
À éviter :
Utiliser les déclarations if de collection pour les simples vérifications de null.
var names = [
'Alice',
if (optionalName != null) optionalName,
'Charlie'
];
À préférer :
Utiliser le préfixe ? en ligne.
var names = ['Alice', ?optionalName, 'Charlie'];
Dot Shorthands
Utilisez les dot shorthands pour omettre le nom de type explicite quand il peut être confiant déduit du contexte, comme avec les enums ou les champs statiques.
À éviter : Qualifier complètement les noms de type quand le type est évident dans le contexte.
LogLevel currentLevel = LogLevel.info;
À préférer : Réduire le bruit visuel avec le shorthand déduit.
LogLevel currentLevel = .info;
Compétences Connexes
- dart-best-practices : Style de code général et idiomes Dart fondamentaux qui précèdent ou complètent les fonctionnalités de syntaxe modernes.