Cours JavaScript et TypeScript · Module 7: TypeScript avancé

Éviter l'imbrication profonde - les retours anticipés

13 min de lecture
Dans cette leçon6

Le Dr Ian Malcolm, assis au centre de contrôle de Jurassic Park, observe le code écrit par l'équipe informatique. « Regardez ça », dit-il en pointant l'écran. « C'est comme traverser un labyrinthe rempli de dinosaures - chaque if est un nouveau virage, chaque else est une impasse. Le chaos trouve toujours un chemin... mais nous ne devrions pas rendre le chemin plus difficile ! »

Le problème : l'anti-motif de la flèche

L'un des problèmes les plus courants dans le code des programmeurs débutants est ce qu'on appelle l'« anti-motif de la flèche » ou la « pyramide du destin » - un code qui s'imbrique de plus en plus profondément, créant une forme caractéristique de flèche :

1// BAD: Arrow Anti-Pattern - code nested like a pyramid
2function checkDinosaurSafety(dinosaur) {
3  if (dinosaur) {
4    if (dinosaur.isAlive) {
5      if (dinosaur.enclosure) {
6        if (dinosaur.enclosure.fence) {
7          if (dinosaur.enclosure.fence.isActive) {
8            if (dinosaur.health > 50) {
9              if (dinosaur.aggressionLevel < 7) {
10                if (dinosaur.lastFed) {
11                  const hoursSinceFeeding = (Date.now() - dinosaur.lastFed) / 3600000;
12                  if (hoursSinceFeeding < 12) {
13                    return 'Safe';
14                  } else {
15                    return 'Hungry - be careful!';
16                  }
17                } else {
18                  return 'No feeding data';
19                }
20              } else {
21                return 'High aggression level!';
22              }
23            } else {
24              return 'Low health level';
25            }
26          } else {
27            return 'ALARM: Fence inactive!';
28          }
29        } else {
30          return 'ERROR: No fence';
31        }
32      } else {
33        return 'ERROR: No assigned enclosure';
34      }
35    } else {
36      return 'Dinosaur deceased';
37    }
38  } else {
39    return 'No dinosaur data';
40  }
41}
42
43// This code is:
44// - Hard to read (you have to count brackets)
45// - Hard to understand (logic is scattered)
46// - Hard to modify (adding a condition requires reorganization)
47// - Error-prone (easy to get lost in the nesting)

La solution : les retours anticipés (clauses de garde)

Plutôt que d'imbriquer le code, nous utilisons des retours anticipés - nous vérifions les conditions d'erreur au début et sortons immédiatement de la fonction. On parle aussi de « clauses de garde » (guard clauses).

1// GOOD: Early Returns - readable linear code
2function checkDinosaurSafety(dinosaur) {
3  // Check all error conditions right away
4  if (!dinosaur) {
5    return 'No dinosaur data';
6  }
7
8  if (!dinosaur.isAlive) {
9    return 'Dinosaur deceased';
10  }
11
12  if (!dinosaur.enclosure) {
13    return 'ERROR: No assigned enclosure';
14  }
15
16  if (!dinosaur.enclosure.fence) {
17    return 'ERROR: No fence';
18  }
19
20  if (!dinosaur.enclosure.fence.isActive) {
21    return 'ALARM: Fence inactive!';
22  }
23
24  if (dinosaur.health <= 50) {
25    return 'Low health level';
26  }
27
28  if (dinosaur.aggressionLevel >= 7) {
29    return 'High aggression level!';
30  }
31
32  if (!dinosaur.lastFed) {
33    return 'No feeding data';
34  }
35
36  // Main logic at the end
37  const hoursSinceFeeding = (Date.now() - dinosaur.lastFed) / 3600000;
38
39  if (hoursSinceFeeding >= 12) {
40    return 'Hungry - be careful!';
41  }
42
43  return 'Safe';
44}
45
46// This code is:
47// Easy to read (linear flow)
48// Easy to understand (each condition is separate)
49// Easy to modify (adding a condition = one line)
50// Less error-prone (no nesting)

Règles pour utiliser les retours anticipés

1. Vérifier d'abord les cas limites et les erreurs

1// BAD: Main logic hidden in nesting
2function processVisitorEntry(visitor, ticket) {
3  if (visitor && ticket) {
4    if (ticket.isValid) {
5      if (ticket.hasAdultSupervision || visitor.age >= 18) {
6        if (visitor.hasHealthClearance) {
7          // Main logic deeply nested
8          registerEntry(visitor);
9          return true;
10        }
11      }
12    }
13  }
14  return false;
15}
16
17// GOOD: Edge cases first
18function processVisitorEntry(visitor, ticket) {
19  // Input data validation
20  if (!visitor || !ticket) {
21    throw new Error('Missing visitor or ticket data');
22  }
23
24  // Checking conditions from most basic
25  if (!ticket.isValid) {
26    throw new Error('Invalid ticket');
27  }
28
29  if (visitor.age < 18 && !ticket.hasAdultSupervision) {
30    throw new Error('Minor requires adult supervision');
31  }
32
33  if (!visitor.hasHealthClearance) {
34    throw new Error('No health clearance');
35  }
36
37  // Main logic at the end - now we know everything is OK
38  registerEntry(visitor);
39  return true;
40}

2. Utiliser la négation pour les conditions d'erreur

1// BAD: Positive conditions lead to nesting
2function feedDinosaur(dinosaurId, foodAmount) {
3  if (dinosaurs.has(dinosaurId)) {
4    const dino = dinosaurs.get(dinosaurId);
5    if (dino.isAlive) {
6      if (foodAmount > 0) {
7        if (foodAmount <= MAX_FOOD_PORTION) {
8          // Feeding logic
9          dino.feed(foodAmount);
10          return true;
11        }
12      }
13    }
14  }
15  return false;
16}
17
18// GOOD: Negating conditions allows early return
19function feedDinosaur(dinosaurId, foodAmount) {
20  // Check all ERROR conditions and exit immediately
21  if (!dinosaurs.has(dinosaurId)) {
22    throw new Error(`Dinosaur ${dinosaurId} does not exist`);
23  }
24
25  const dino = dinosaurs.get(dinosaurId);
26
27  if (!dino.isAlive) {
28    throw new Error('Cannot feed a dead dinosaur');
29  }
30
31  if (foodAmount <= 0) {
32    throw new Error('Food amount must be greater than 0');
33  }
34
35  if (foodAmount > MAX_FOOD_PORTION) {
36    throw new Error(`Maximum portion is ${MAX_FOOD_PORTION}kg`);
37  }
38
39  // Main logic - we know everything is OK
40  dino.feed(foodAmount);
41  return true;
42}

3. Extraire la logique complexe dans des fonctions séparées

1// BAD: Complex logic nested
2function authorizeAccess(employee, sector) {
3  if (employee) {
4    if (employee.isActive) {
5      if (employee.clearanceLevel >= sector.requiredClearance) {
6        if (employee.department === sector.allowedDepartment ||
7            employee.role === 'Admin' ||
8            employee.temporaryAccess.includes(sector.id)) {
9          if (!employee.accessRestrictions.includes(sector.id)) {
10            if (isWithinAllowedHours(employee.schedule, sector.operatingHours)) {
11              return grantAccess(employee, sector);
12            }
13          }
14        }
15      }
16    }
17  }
18  return denyAccess(employee, sector);
19}
20
21// GOOD: Extract conditions into helper functions
22function authorizeAccess(employee, sector) {
23  if (!employee) {
24    return denyAccess(null, sector, 'No employee data');
25  }
26
27  if (!employee.isActive) {
28    return denyAccess(employee, sector, 'Account inactive');
29  }
30
31  if (!hasSufficientClearance(employee, sector)) {
32    return denyAccess(employee, sector, 'Insufficient clearance level');
33  }
34
35  if (!isDepartmentAuthorized(employee, sector)) {
36    return denyAccess(employee, sector, 'Unauthorized department');
37  }
38
39  if (hasAccessRestriction(employee, sector)) {
40    return denyAccess(employee, sector, 'Access restriction');
41  }
42
43  if (!isWithinAllowedHours(employee.schedule, sector.operatingHours)) {
44    return denyAccess(employee, sector, 'Outside allowed hours');
45  }
46
47  return grantAccess(employee, sector);
48}
49
50// Helper functions - each with a single responsibility
51function hasSufficientClearance(employee, sector) {
52  return employee.clearanceLevel >= sector.requiredClearance;
53}
54
55function isDepartmentAuthorized(employee, sector) {
56  return employee.department === sector.allowedDepartment ||
57         employee.role === 'Admin' ||
58         employee.temporaryAccess.includes(sector.id);
59}
60
61function hasAccessRestriction(employee, sector) {
62  return employee.accessRestrictions.includes(sector.id);
63}

Exemples pratiques de refactorisation

Exemple 1 : système d'alarme

1// BEFORE: Nested code hard to read
2function triggerAlarm(sensorData) {
3  if (sensorData) {
4    if (sensorData.fence) {
5      if (sensorData.fence.voltage < MIN_VOLTAGE) {
6        if (emergencyProtocol.isActive) {
7          if (securityTeam.isAvailable) {
8            if (evacuationRoutes.areOpen) {
9              activateFullAlarm();
10              notifySecurityTeam();
11              return { status: 'active', level: 'critical' };
12            } else {
13              activatePartialAlarm();
14              return { status: 'active', level: 'warning' };
15            }
16          } else {
17            activateAutomatedResponse();
18            return { status: 'automated', level: 'critical' };
19          }
20        } else {
21          logIncident();
22          return { status: 'logged', level: 'info' };
23        }
24      } else {
25        return { status: 'normall', level: 'info' };
26      }
27    }
28  }
29  return { status: 'error', level: 'critical' };
30}
31
32// AFTER: Readable code with early returns
33function triggerAlarm(sensorData) {
34  // Data validation
35  if (!sensorData || !sensorData.fence) {
36    return { status: 'error', level: 'critical', message: 'No sensor data' };
37  }
38
39  // Check main condition
40  if (sensorData.fence.voltage >= MIN_VOLTAGE) {
41    return { status: 'normall', level: 'info', message: 'Voltage normall' };
42  }
43
44  // Low voltage detected - check protocols
45  if (!emergencyProtocol.isActive) {
46    logIncident();
47    return { status: 'logged', level: 'info', message: 'Incident logged' };
48  }
49
50  // Protocol active - check team availability
51  if (!securityTeam.isAvailable) {
52    activateAutomatedResponse();
53    return { status: 'automated', level: 'critical', message: 'Automated response' };
54  }
55
56  // Team available - check evacuation routes
57  if (!evacuationRoutes.areOpen) {
58    activatePartialAlarm();
59    return { status: 'active', level: 'warning', message: 'Partial alarm' };
60  }
61
62  // Everything ready - full alarm
63  activateFullAlarm();
64  notifySecurityTeam();
65  return { status: 'active', level: 'critical', message: 'Full alarm activated' };
66}

Exemple 2 : validation de formulaire

1// BEFORE: Validation with many nesting levels
2function validateVisitorRegistration(formData) {
3  if (formData.firstName) {
4    if (formData.lastName) {
5      if (formData.email) {
6        if (isValidEmail(formData.email)) {
7          if (formData.age) {
8            if (formData.age >= 5 && formData.age <= 120) {
9              if (formData.emergencyContact) {
10                if (formData.emergencyContact.phone) {
11                  if (isValidPhone(formData.emergencyContact.phone)) {
12                    if (formData.acceptedTerms) {
13                      return { valid: true, data: formData };
14                    } else {
15                      return { valid: false, error: 'You must accept the terms' };
16                    }
17                  } else {
18                    return { valid: false, error: 'Invalid emergency phone number' };
19                  }
20                } else {
21                  return { valid: false, error: 'Missing emergency phone number' };
22                }
23              } else {
24                return { valid: false, error: 'Missing emergency contact' };
25              }
26            } else {
27              return { valid: false, error: 'Age must be between 5 and 120' };
28            }
29          } else {
30            return { valid: false, error: 'Missing age' };
31          }
32        } else {
33          return { valid: false, error: 'Invalid email address' };
34        }
35      } else {
36        return { valid: false, error: 'Missing email address' };
37      }
38    } else {
39      return { valid: false, error: 'Missing last name' };
40    }
41  } else {
42    return { valid: false, error: 'Missing first name' };
43  }
44}
45
46// AFTER: Readable validation with early returns
47function validateVisitorRegistration(formData) {
48  // Validate required text fields
49  if (!formData.firstName) {
50    return { valid: false, error: 'Missing first name' };
51  }
52
53  if (!formData.lastName) {
54    return { valid: false, error: 'Missing last name' };
55  }
56
57  if (!formData.email) {
58    return { valid: false, error: 'Missing email address' };
59  }
60
61  if (!isValidEmail(formData.email)) {
62    return { valid: false, error: 'Invalid email address' };
63  }
64
65  // Age validation
66  if (!formData.age) {
67    return { valid: false, error: 'Missing age' };
68  }
69
70  if (formData.age < 5 || formData.age > 120) {
71    return { valid: false, error: 'Age must be between 5 and 120' };
72  }
73
74  // Emergency contact validation
75  if (!formData.emergencyContact) {
76    return { valid: false, error: 'Missing emergency contact' };
77  }
78
79  if (!formData.emergencyContact.phone) {
80    return { valid: false, error: 'Missing emergency phone number' };
81  }
82
83  if (!isValidPhone(formData.emergencyContact.phone)) {
84    return { valid: false, error: 'Invalid emergency phone number' };
85  }
86
87  // Terms acceptance validation
88  if (!formData.acceptedTerms) {
89    return { valid: false, error: 'You must accept the terms' };
90  }
91
92  // All validations passed
93  return { valid: true, data: formData };
94}

Exemple 3 : traitement de commande

1// BEFORE: Complex business logic in nesting
2function processFoodOrder(order) {
3  if (order && order.items && order.items.length > 0) {
4    if (inventory.checkAvailability(order.items)) {
5      const total = calculateTotal(order.items);
6      if (order.payment) {
7        if (order.payment.amount >= total) {
8          if (order.deliveryLocation) {
9            if (isDeliveryLocationSafe(order.deliveryLocation)) {
10              if (deliveryPersonnel.isAvailable()) {
11                const receipt = generateReceipt(order, total);
12                inventory.reduceStock(order.items);
13                scheduleDelivery(order);
14                return { success: true, receipt };
15              } else {
16                return { success: false, reason: 'No available delivery personnel' };
17              }
18            } else {
19              return { success: false, reason: 'Unsafe delivery location' };
20            }
21          } else {
22            return { success: false, reason: 'Missing delivery location' };
23          }
24        } else {
25          return { success: false, reason: 'Insufficient payment amount' };
26        }
27      } else {
28        return { success: false, reason: 'Missing payment data' };
29      }
30    } else {
31      return { success: false, reason: 'Products unavailable in stock' };
32    }
33  } else {
34    return { success: false, reason: 'Invalid order' };
35  }
36}
37
38// AFTER: Business logic divided into steps
39function processFoodOrder(order) {
40  // Step 1: Order validation
41  const validationError = validateOrder(order);
42  if (validationError) {
43    return { success: false, reason: validationError };
44  }
45
46  // Step 2: Check stock availability
47  if (!inventory.checkAvailability(order.items)) {
48    return { success: false, reason: 'Products unavailable in stock' };
49  }
50
51  // Step 3: Payment verification
52  const total = calculateTotal(order.items);
53  const paymentError = validatePayment(order.payment, total);
54  if (paymentError) {
55    return { success: false, reason: paymentError };
56  }
57
58  // Step 4: Delivery verification
59  const deliveryError = validateDelivery(order.deliveryLocation);
60  if (deliveryError) {
61    return { success: false, reason: deliveryError };
62  }
63
64  if (!deliveryPersonnel.isAvailable()) {
65    return { success: false, reason: 'No available delivery personnel' };
66  }
67
68  // Step 5: Order fulfillment
69  const receipt = generateReceipt(order, total);
70  inventory.reduceStock(order.items);
71  scheduleDelivery(order);
72
73  return { success: true, receipt };
74}
75
76// Helper functions
77function validateOrder(order) {
78  if (!order) return 'Missing order';
79  if (!order.items || order.items.length === 0) return 'No products in order';
80  return null;
81}
82
83function validatePayment(payment, requiredAmount) {
84  if (!payment) return 'Missing payment data';
85  if (payment.amount < requiredAmount) return 'Insufficient payment amount';
86  return null;
87}
88
89function validateDelivery(location) {
90  if (!location) return 'Missing delivery location';
91  if (!isDeliveryLocationSafe(location)) return 'Unsafe delivery location';
92  return null;
93}

Techniques supplémentaires

1. Utiliser le chaînage optionnel et la coalescence des nuls

1// BEFORE: Nested null/undefined checking
2function getDinosaurLocation(dinosaurId) {
3  const dino = dinosaurs.get(dinosaurId);
4  if (dino) {
5    if (dino.enclosure) {
6      if (dino.enclosure.location) {
7        return dino.enclosure.location.coordinates;
8      }
9    }
10  }
11  return null;
12}
13
14// AFTER: Optional chaining
15function getDinosaurLocation(dinosaurId) {
16  const dino = dinosaurs.get(dinosaurId);
17  return dino?.enclosure?.location?.coordinates ?? null;
18}

2. Extraire les conditions complexes dans des variables

1// BEFORE: Hard to understand condition
2function shouldEvacuate(park) {
3  if ((park.fenceStatus === 'breached' && park.dangerousDinosaurs > 0) ||
4      (park.powerOutage && park.backupGeneratorFailed) ||
5      (park.weatherAlert === 'severe' && park.visitorCount > 100)) {
6    return true;
7  }
8  return false;
9}
10
11// AFTER: Named conditions
12function shouldEvacuate(park) {
13  const hasDangerousBreach = park.fenceStatus === 'breached' && park.dangerousDinosaurs > 0;
14  const hasTotalPowerFailure = park.powerOutage && park.backupGeneratorFailed;
15  const hasWeatherEmergency = park.weatherAlert === 'severe' && park.visitorCount > 100;
16
17  return hasDangerousBreach || hasTotalPowerFailure || hasWeatherEmergency;
18}

3. Utiliser le polymorphisme (stratégie) plutôt que switch/if-else

1// BEFORE: Long if-else chain
2function handleDinosaurBehavior(dinosaur, behavior) {
3  if (behavior === 'aggressive') {
4    activateDefenses();
5    alertSecurity();
6    evacuateVisitors();
7  } else if (behavior === 'scared') {
8    reduceNoiseLevel();
9    increaseDistance();
10  } else if (behavior === 'hungry') {
11    prepareFoodration();
12    scheduleFeedingTime();
13  } else if (behavior === 'sleeping') {
14    reduceActivity();
15    dimLights();
16  } else if (behavior === 'playful') {
17    allowInteraction();
18    monitorClosely();
19  } else {
20    logUnknownBehavior(behavior);
21  }
22}
23
24// AFTER: Strategy with a function map
25const behaviorHandlers = {
26  aggressive: () => {
27    activateDefenses();
28    alertSecurity();
29    evacuateVisitors();
30  },
31  scared: () => {
32    reduceNoiseLevel();
33    increaseDistance();
34  },
35  hungry: () => {
36    prepareFoodRation();
37    scheduleFeedingTime();
38  },
39  sleeping: () => {
40    reduceActivity();
41    dimLights();
42  },
43  playful: () => {
44    allowInteraction();
45    monitorClosely();
46  }
47};
48
49function handleDinosaurBehavior(dinosaur, behavior) {
50  const handler = behaviorHandlers[behavior];
51
52  if (!handler) {
53    logUnknownBehavior(behavior);
54    return;
55  }
56
57  handler();
58}

Résumé

« La vie... trouve toujours un chemin », dit le Dr Malcolm. « Mais le code ne devrait pas être un labyrinthe. Chaque fonction devrait avoir un chemin clair et linéaire - de la validation au début jusqu'à la logique métier à la fin. »

Principes clés :

  1. Vérifie d'abord les erreurs - utilise les retours anticipés pour les cas limites
  2. Inverse les conditions - au lieu de if (x), utilise if (!x) return
  3. Évite l'imbrication - maximum 2 à 3 niveaux
  4. Extrais la logique - conditions complexes -> fonctions auxiliaires
  5. Nomme les conditions - assigne les expressions complexes à des variables
  6. Utilise le JS moderne - chaînage optionnel, coalescence des nuls

Un code avec des retours anticipés est :

  • Plus lisible - le flux est plus facile à comprendre
  • Plus facile à maintenir - on ajoute facilement une nouvelle condition
  • Moins sujet aux erreurs - moins d'endroits où les choses peuvent mal tourner
  • Plus testable - chaque condition peut être testée séparément

Souviens-toi : un bon code n'est pas seulement un code qui fonctionne - c'est un code facile à comprendre et à modifier. Et à Jurassic Park, où chaque erreur peut avoir des conséquences catastrophiques, la lisibilité du code peut sauver des vies !

Code de cette leçon : index.ts
1// Éviter l'imbrication profonde - Early Returns
2// Jurassic Park : système de sécurité avec clauses de garde
3console.log("=== EARLY RETURNS - PARC JURASSIQUE ===\n");
4
5// ===========================================
6// 1. Exemple : vérification de la sécurité d'un dinosaure
7// ===========================================
8console.log("=== 1. VÉRIFICATION DE LA SÉCURITÉ ===");
9
10interface DinosaurData {
11  name: string;
12  isAlive: boolean;
13  health: number;
14  aggressionLevel: number;
15  enclosure?: {
16    fence?: {
17      isActive: boolean;
18      voltage: number;
19    };
20  };
21  lastFed?: number;
22}
23
24// Retours anticipés - chaque condition vérifiée séparément
25function checkSafety(dino: DinosaurData): string {
26  if (!dino) {
27    return "ERREUR: Aucune donnée sur le dinosaure";
28  }
29
30  if (!dino.isAlive) {
31    return "Dinosaur mort - aucun danger";
32  }
33
34  if (!dino.enclosure?.fence) {
35    return "ALARM: Fence manquante !";
36  }
37
38  if (!dino.enclosure.fence.isActive) {
39    return "ALARM: Fence inactive !";
40  }
41
42  if (dino.health <= 30) {
43    return "AVERTISSEMENT: Niveau de santé bas";
44  }
45
46  if (dino.aggressionLevel >= 8) {
47    return "ALARM: Niveau d'agressivité très élevé !";
48  }
49
50  return "Status: Sécurisé";
51}
52
53const trex: DinosaurData = {
54  name: "T-Rex",
55  isAlive: true,
56  health: 85,
57  aggressionLevel: 6,
58  enclosure: { fence: { isActive: true, voltage: 10000 } },
59  lastFed: Date.now() - 3600000
60};
61
62const brokenFence: DinosaurData = {
63  name: "Velociraptor",
64  isAlive: true,
65  health: 90,
66  aggressionLevel: 9,
67  enclosure: { fence: { isActive: false, voltage: 0 } }
68};
69
70console.log(`${trex.name}: ${checkSafety(trex)}`);
71console.log(`${brokenFence.name}: ${checkSafety(brokenFence)}`);
72
73// ===========================================
74// 2. Validation avec retours anticipés
75// ===========================================
76console.log("\n=== 2. VALIDATION DU FORMULAIRE ===");
77
78interface VisitorForm {
79  firstName?: string;
80  lastName?: string;
81  age?: number;
82  email?: string;
83  acceptedTerms?: boolean;
84}
85
86function validateVisitor(form: VisitorForm): { valid: boolean; error?: string } {
87  if (!form.firstName) {
88    return { valid: false, error: "Prénom manquant" };
89  }
90
91  if (!form.lastName) {
92    return { valid: false, error: "Nom manquant" };
93  }
94
95  if (!form.age || form.age < 5) {
96    return { valid: false, error: "Âge minimum: 5 years old" };
97  }
98
99  if (!form.email || !form.email.includes("@")) {
100    return { valid: false, error: "Email invalide" };
101  }
102
103  if (!form.acceptedTerms) {
104    return { valid: false, error: "Acceptation des conditions requise" };
105  }
106
107  return { valid: true };
108}
109
110const goodForm: VisitorForm = {
111  firstName: "Jean",
112  lastName: "Dupont",
113  age: 25,
114  email: "jean@parc.com",
115  acceptedTerms: true
116};
117
118const badForm: VisitorForm = {
119  firstName: "Anna",
120  age: 3
121};
122
123console.log("Formulaire valide:", validateVisitor(goodForm));
124console.log("Formulaire invalide:", validateVisitor(badForm));
125
126// ===========================================
127// 3. TODO : Essaie toi-même !
128// ===========================================
129console.log("\n=== 3. À TOI DE JOUER ===");
130console.log("Refactorise le code suivant en utilisant des early returns:");
131
132// TODO : Réécris cette fonction en utilisant des retours anticipés
133// au lieu de if-else imbriqués
134
135function processOrder(items: string[], payment: number) {
136  // Utilise des retours anticipés au lieu de conditions imbriquées !
137  if (items && items.length > 0) {
138    if (payment > 0) {
139      if (payment >= items.length * 10) {
140        console.log("Commande traitée !");
141        return true;
142      } else {
143        console.log("Montant insuffisant");
144        return false;
145      }
146    } else {
147      console.log("Paiement manquant");
148      return false;
149    }
150  } else {
151    console.log("Aucun produit");
152    return false;
153  }
154}
155
156processOrder(["Billet VIP", "Lunch"], 50);
157processOrder([], 10);
158processOrder(["Billet"], 5);
159
160console.log("\nAstuce: Utilise la négation des conditions et return au début !");

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Qu'est-ce que l'« anti-pattern de la flèche » (Pyramid of Doom) dans le code ?

  2. 2. Que retourne dino?.enclosure?.location ?? null quand dino vaut undefined ?

Tâches pratiques dans le jeu

  • Tri vertical

    Ordonne les éléments d'une fonction qui utilise des retours anticipés (de haut en bas) :

  • Clique dans l'ordre

    Ordonne les éléments d’une clause de garde :

Articles utiles