Kurs JavaScript i TypeScript · Moduł 7: Zaawansowany TypeScript

Unikanie głębokiego zagnieżdżania - Early Returns

12 min czytania ·
W tej lekcji6

Dr. Ian Malcolm, siedząc w centrum kontroli Parku Jurajskiego, obserwuje kod napisany przez zespół IT. "Spójrzcie na to," mówi, wskazując na ekran. "To tak jakby chodzić przez labirynt pełen dinozaurów - każdy if to kolejny zakręt, każdy else to ślepa uliczka. Chaos znajduje drogę... ale my nie powinniśmy jej utrudniać!"

Problem: Arrow Anti-Pattern

Jednym z najczęstszych problemów w kodzie początkujących programistów jest tzw. "Arrow Anti-Pattern" lub "Pyramid of Doom" - kod, który zagnieżdża się coraz głębiej, tworząc charakterystyczny kształt strzałki:

// ŹLE: Arrow Anti-Pattern - kod zagnieżdżony jak piramida
function checkDinosaurSafety(dinosaur) {
  if (dinosaur) {
    if (dinosaur.isAlive) {
      if (dinosaur.enclosure) {
        if (dinosaur.enclosure.fence) {
          if (dinosaur.enclosure.fence.isActive) {
            if (dinosaur.health > 50) {
              if (dinosaur.aggressionLevel < 7) {
                if (dinosaur.lastFed) {
                  const hoursSinceFeeding = (Date.now() - dinosaur.lastFed) / 3600000;
                  if (hoursSinceFeeding < 12) {
                    return 'Bezpieczny';
                  } else {
                    return 'Głodny - ostrożnie!';
                  }
                } else {
                  return 'Brak danych o karmieniu';
                }
              } else {
                return 'Wysoki poziom agresji!';
              }
            } else {
              return 'Niski poziom zdrowia';
            }
          } else {
            return 'ALARM: Ogrodzenie nieaktywne!';
          }
        } else {
          return 'BŁĄD: Brak ogrodzenia';
        }
      } else {
        return 'BŁĄD: Brak przypisanego wybiegu';
      }
    } else {
      return 'Dinozaur martwy';
    }
  } else {
    return 'Brak danych o dinozaurze';
  }
}

// Ten kod jest:
// - Trudny do przeczytania (musisz liczyć nawiasy)
// - Trudny do zrozumienia (logika jest rozproszona)
// - Trudny do modyfikacji (dodanie warunku wymaga reorganizacji)
// - Podatny na błędy (łatwo się zgubić w zagnieżdżeniach)

Rozwiązanie: Early Returns (Guard Clauses)

Zamiast zagnieżdżać kod, używamy early returns - sprawdzamy warunki błędów na początku i od razu wychodzimy z funkcji. To nazywa się także "guard clauses" (klauzule strażnicze).

// DOBRZE: Early Returns - czytelny kod liniowy
function checkDinosaurSafety(dinosaur) {
  // Sprawdzamy wszystkie warunki błędów od razu
  if (!dinosaur) {
    return 'Brak danych o dinozaurze';
  }

  if (!dinosaur.isAlive) {
    return 'Dinozaur martwy';
  }

  if (!dinosaur.enclosure) {
    return 'BŁĄD: Brak przypisanego wybiegu';
  }

  if (!dinosaur.enclosure.fence) {
    return 'BŁĄD: Brak ogrodzenia';
  }

  if (!dinosaur.enclosure.fence.isActive) {
    return 'ALARM: Ogrodzenie nieaktywne!';
  }

  if (dinosaur.health <= 50) {
    return 'Niski poziom zdrowia';
  }

  if (dinosaur.aggressionLevel >= 7) {
    return 'Wysoki poziom agresji!';
  }

  if (!dinosaur.lastFed) {
    return 'Brak danych o karmieniu';
  }

  // Główna logika na końcu
  const hoursSinceFeeding = (Date.now() - dinosaur.lastFed) / 3600000;

  if (hoursSinceFeeding >= 12) {
    return 'Głodny - ostrożnie!';
  }

  return 'Bezpieczny';
}

// Ten kod jest:
// Łatwy do przeczytania (liniowy flow)
// Łatwy do zrozumienia (każdy warunek jest osobno)
// Łatwy do modyfikacji (dodanie warunku = jedna linijka)
// Mniej podatny na błędy (brak zagnieżdżeń)

Zasady stosowania Early Returns

1. Sprawdzaj najpierw edge cases i błędy

// ŹLE: Główna logika ukryta w zagnieżdżeniach
function processVisitorEntry(visitor, ticket) {
  if (visitor && ticket) {
    if (ticket.isValid) {
      if (ticket.hasAdultSupervision || visitor.age >= 18) {
        if (visitor.hasHealthClearance) {
          // Główna logika głęboko zagnieżdżona
          registerEntry(visitor);
          return true;
        }
      }
    }
  }
  return false;
}

// DOBRZE: Edge cases na początku
function processVisitorEntry(visitor, ticket) {
  // Walidacja danych wejściowych
  if (!visitor || !ticket) {
    throw new Error('Brak danych odwiedzającego lub biletu');
  }

  // Sprawdzanie warunków od najbardziej podstawowych
  if (!ticket.isValid) {
    throw new Error('Bilet nieważny');
  }

  if (visitor.age < 18 && !ticket.hasAdultSupervision) {
    throw new Error('Niepełnoletni wymaga opieki osoby dorosłej');
  }

  if (!visitor.hasHealthClearance) {
    throw new Error('Brak zezwolenia zdrowotnego');
  }

  // Główna logika na końcu - teraz wiadomo że wszystko jest OK
  registerEntry(visitor);
  return true;
}

2. Używaj negacji dla warunków błędnych

// ŹLE: Pozytywne warunki prowadzą do zagnieżdżeń
function feedDinosaur(dinosaurId, foodAmount) {
  if (dinosaurs.has(dinosaurId)) {
    const dino = dinosaurs.get(dinosaurId);
    if (dino.isAlive) {
      if (foodAmount > 0) {
        if (foodAmount <= MAX_FOOD_PORTION) {
          // Logika karmienia
          dino.feed(foodAmount);
          return true;
        }
      }
    }
  }
  return false;
}

// DOBRZE: Negacja warunków pozwala na early return
function feedDinosaur(dinosaurId, foodAmount) {
  // Sprawdzamy wszystkie warunki BŁĘDNE i od razu wychodzimy
  if (!dinosaurs.has(dinosaurId)) {
    throw new Error(`Dinozaur ${dinosaurId} nie istnieje`);
  }

  const dino = dinosaurs.get(dinosaurId);

  if (!dino.isAlive) {
    throw new Error('Nie można karmić martwego dinozaura');
  }

  if (foodAmount <= 0) {
    throw new Error('Ilość jedzenia musi być większa niż 0');
  }

  if (foodAmount > MAX_FOOD_PORTION) {
    throw new Error(`Maksymalna porcja to ${MAX_FOOD_PORTION}kg`);
  }

  // Główna logika - wiemy że wszystko jest OK
  dino.feed(foodAmount);
  return true;
}

3. Wynoś złożoną logikę do osobnych funkcji

// ŹLE: Złożona logika zagnieżdżona
function authorizeAccess(employee, sector) {
  if (employee) {
    if (employee.isActive) {
      if (employee.clearanceLevel >= sector.requiredClearance) {
        if (employee.department === sector.allowedDepartment ||
            employee.role === 'Admin' ||
            employee.temporaryAccess.includes(sector.id)) {
          if (!employee.accessRestrictions.includes(sector.id)) {
            if (isWithinAllowedHours(employee.schedule, sector.operatingHours)) {
              return grantAccess(employee, sector);
            }
          }
        }
      }
    }
  }
  return denyAccess(employee, sector);
}

// DOBRZE: Wydzielamy warunki do funkcji pomocniczych
function authorizeAccess(employee, sector) {
  if (!employee) {
    return denyAccess(null, sector, 'Brak danych pracownika');
  }

  if (!employee.isActive) {
    return denyAccess(employee, sector, 'Konto nieaktywne');
  }

  if (!hasSufficientClearance(employee, sector)) {
    return denyAccess(employee, sector, 'Niewystarczający poziom dostępu');
  }

  if (!isDepartmentAuthorized(employee, sector)) {
    return denyAccess(employee, sector, 'Nieautoryzowany dział');
  }

  if (hasAccessRestriction(employee, sector)) {
    return denyAccess(employee, sector, 'Ograniczenie dostępu');
  }

  if (!isWithinAllowedHours(employee.schedule, sector.operatingHours)) {
    return denyAccess(employee, sector, 'Poza godzinami dostępu');
  }

  return grantAccess(employee, sector);
}

// Funkcje pomocnicze - każda ma pojedynczą odpowiedzialność
function hasSufficientClearance(employee, sector) {
  return employee.clearanceLevel >= sector.requiredClearance;
}

function isDepartmentAuthorized(employee, sector) {
  return employee.department === sector.allowedDepartment ||
         employee.role === 'Admin' ||
         employee.temporaryAccess.includes(sector.id);
}

function hasAccessRestriction(employee, sector) {
  return employee.accessRestrictions.includes(sector.id);
}

Praktyczne przykłady refaktoryzacji

Przykład 1: System alarmowy

// PRZED: Zagnieżdżony kod trudny do czytania
function triggerAlarm(sensorData) {
  if (sensorData) {
    if (sensorData.fence) {
      if (sensorData.fence.voltage < MIN_VOLTAGE) {
        if (emergencyProtocol.isActive) {
          if (securityTeam.isAvailable) {
            if (evacuationRoutes.areOpen) {
              activateFullAlarm();
              notifySecurityTeam();
              return { status: 'active', level: 'critical' };
            } else {
              activatePartialAlarm();
              return { status: 'active', level: 'warning' };
            }
          } else {
            activateAutomatedResponse();
            return { status: 'automated', level: 'critical' };
          }
        } else {
          logIncident();
          return { status: 'logged', level: 'info' };
        }
      } else {
        return { status: 'normal', level: 'info' };
      }
    }
  }
  return { status: 'error', level: 'critical' };
}

// PO: Czytelny kod z early returns
function triggerAlarm(sensorData) {
  // Walidacja danych
  if (!sensorData || !sensorData.fence) {
    return { status: 'error', level: 'critical', message: 'Brak danych z czujnika' };
  }

  // Sprawdzenie głównego warunku
  if (sensorData.fence.voltage >= MIN_VOLTAGE) {
    return { status: 'normal', level: 'info', message: 'Napięcie prawidłowe' };
  }

  // Niskie napięcie wykryte - sprawdzamy protokoły
  if (!emergencyProtocol.isActive) {
    logIncident();
    return { status: 'logged', level: 'info', message: 'Incydent zarejestrowany' };
  }

  // Protokół aktywny - sprawdzamy dostępność zespołu
  if (!securityTeam.isAvailable) {
    activateAutomatedResponse();
    return { status: 'automated', level: 'critical', message: 'Odpowiedź automatyczna' };
  }

  // Zespół dostępny - sprawdzamy drogi ewakuacyjne
  if (!evacuationRoutes.areOpen) {
    activatePartialAlarm();
    return { status: 'active', level: 'warning', message: 'Alarm częściowy' };
  }

  // Wszystko gotowe - pełny alarm
  activateFullAlarm();
  notifySecurityTeam();
  return { status: 'active', level: 'critical', message: 'Pełny alarm aktywowany' };
}

Przykład 2: Walidacja formularza

// PRZED: Walidacja z wieloma poziomami zagnieżdżeń
function validateVisitorRegistration(formData) {
  if (formData.firstName) {
    if (formData.lastName) {
      if (formData.email) {
        if (isValidEmail(formData.email)) {
          if (formData.age) {
            if (formData.age >= 5 && formData.age <= 120) {
              if (formData.emergencyContact) {
                if (formData.emergencyContact.phone) {
                  if (isValidPhone(formData.emergencyContact.phone)) {
                    if (formData.acceptedTerms) {
                      return { valid: true, data: formData };
                    } else {
                      return { valid: false, error: 'Musisz zaakceptować regulamin' };
                    }
                  } else {
                    return { valid: false, error: 'Nieprawidłowy numer telefonu awaryjnego' };
                  }
                } else {
                  return { valid: false, error: 'Brak numeru telefonu awaryjnego' };
                }
              } else {
                return { valid: false, error: 'Brak kontaktu awaryjnego' };
              }
            } else {
              return { valid: false, error: 'Wiek musi być między 5 a 120' };
            }
          } else {
            return { valid: false, error: 'Brak wieku' };
          }
        } else {
          return { valid: false, error: 'Nieprawidłowy adres email' };
        }
      } else {
        return { valid: false, error: 'Brak adresu email' };
      }
    } else {
      return { valid: false, error: 'Brak nazwiska' };
    }
  } else {
    return { valid: false, error: 'Brak imienia' };
  }
}

// PO: Czytelna walidacja z early returns
function validateVisitorRegistration(formData) {
  // Walidacja wymaganych pól tekstowych
  if (!formData.firstName) {
    return { valid: false, error: 'Brak imienia' };
  }

  if (!formData.lastName) {
    return { valid: false, error: 'Brak nazwiska' };
  }

  if (!formData.email) {
    return { valid: false, error: 'Brak adresu email' };
  }

  if (!isValidEmail(formData.email)) {
    return { valid: false, error: 'Nieprawidłowy adres email' };
  }

  // Walidacja wieku
  if (!formData.age) {
    return { valid: false, error: 'Brak wieku' };
  }

  if (formData.age < 5 || formData.age > 120) {
    return { valid: false, error: 'Wiek musi być między 5 a 120' };
  }

  // Walidacja kontaktu awaryjnego
  if (!formData.emergencyContact) {
    return { valid: false, error: 'Brak kontaktu awaryjnego' };
  }

  if (!formData.emergencyContact.phone) {
    return { valid: false, error: 'Brak numeru telefonu awaryjnego' };
  }

  if (!isValidPhone(formData.emergencyContact.phone)) {
    return { valid: false, error: 'Nieprawidłowy numer telefonu awaryjnego' };
  }

  // Walidacja akceptacji regulaminu
  if (!formData.acceptedTerms) {
    return { valid: false, error: 'Musisz zaakceptować regulamin' };
  }

  // Wszystkie walidacje przeszły
  return { valid: true, data: formData };
}

Przykład 3: Przetwarzanie zamówienia

// PRZED: Złożona logika biznesowa w zagnieżdżeniach
function processFoodOrder(order) {
  if (order && order.items && order.items.length > 0) {
    if (inventory.checkAvailability(order.items)) {
      const total = calculateTotal(order.items);
      if (order.payment) {
        if (order.payment.amount >= total) {
          if (order.deliveryLocation) {
            if (isDeliveryLocationSafe(order.deliveryLocation)) {
              if (deliveryPersonnel.isAvailable()) {
                const receipt = generateReceipt(order, total);
                inventory.reduceStock(order.items);
                scheduleDelivery(order);
                return { success: true, receipt };
              } else {
                return { success: false, reason: 'Brak dostępnego personelu' };
              }
            } else {
              return { success: false, reason: 'Niebezpieczna lokalizacja dostawy' };
            }
          } else {
            return { success: false, reason: 'Brak lokalizacji dostawy' };
          }
        } else {
          return { success: false, reason: 'Niewystarczająca kwota płatności' };
        }
      } else {
        return { success: false, reason: 'Brak danych płatności' };
      }
    } else {
      return { success: false, reason: 'Produkty niedostępne w magazynie' };
    }
  } else {
    return { success: false, reason: 'Nieprawidłowe zamówienie' };
  }
}

// PO: Logika biznesowa podzielona na kroki
function processFoodOrder(order) {
  // Krok 1: Walidacja zamówienia
  const validationError = validateOrder(order);
  if (validationError) {
    return { success: false, reason: validationError };
  }

  // Krok 2: Sprawdzenie dostępności w magazynie
  if (!inventory.checkAvailability(order.items)) {
    return { success: false, reason: 'Produkty niedostępne w magazynie' };
  }

  // Krok 3: Weryfikacja płatności
  const total = calculateTotal(order.items);
  const paymentError = validatePayment(order.payment, total);
  if (paymentError) {
    return { success: false, reason: paymentError };
  }

  // Krok 4: Weryfikacja dostawy
  const deliveryError = validateDelivery(order.deliveryLocation);
  if (deliveryError) {
    return { success: false, reason: deliveryError };
  }

  if (!deliveryPersonnel.isAvailable()) {
    return { success: false, reason: 'Brak dostępnego personelu' };
  }

  // Krok 5: Realizacja zamówienia
  const receipt = generateReceipt(order, total);
  inventory.reduceStock(order.items);
  scheduleDelivery(order);

  return { success: true, receipt };
}

// Funkcje pomocnicze
function validateOrder(order) {
  if (!order) return 'Brak zamówienia';
  if (!order.items || order.items.length === 0) return 'Brak produktów w zamówieniu';
  return null;
}

function validatePayment(payment, requiredAmount) {
  if (!payment) return 'Brak danych płatności';
  if (payment.amount < requiredAmount) return 'Niewystarczająca kwota płatności';
  return null;
}

function validateDelivery(location) {
  if (!location) return 'Brak lokalizacji dostawy';
  if (!isDeliveryLocationSafe(location)) return 'Niebezpieczna lokalizacja dostawy';
  return null;
}

Dodatkowe techniki

1. Używaj optional chaining i nullish coalescing

// PRZED: Zagnieżdżone sprawdzanie null/undefined
function getDinosaurLocation(dinosaurId) {
  const dino = dinosaurs.get(dinosaurId);
  if (dino) {
    if (dino.enclosure) {
      if (dino.enclosure.location) {
        return dino.enclosure.location.coordinates;
      }
    }
  }
  return null;
}

// PO: Optional chaining
function getDinosaurLocation(dinosaurId) {
  const dino = dinosaurs.get(dinosaurId);
  return dino?.enclosure?.location?.coordinates ?? null;
}

2. Wydzielaj złożone warunki do zmiennych

// PRZED: Trudny do zrozumienia warunek
function shouldEvacuate(park) {
  if ((park.fenceStatus === 'breached' && park.dangerousDinosaurs > 0) ||
      (park.powerOutage && park.backupGeneratorFailed) ||
      (park.weatherAlert === 'severe' && park.visitorCount > 100)) {
    return true;
  }
  return false;
}

// PO: Nazwane warunki
function shouldEvacuate(park) {
  const hasDangerousBreach = park.fenceStatus === 'breached' && park.dangerousDinosaurs > 0;
  const hasTotalPowerFailure = park.powerOutage && park.backupGeneratorFailed;
  const hasWeatherEmergency = park.weatherAlert === 'severe' && park.visitorCount > 100;

  return hasDangerousBreach || hasTotalPowerFailure || hasWeatherEmergency;
}

3. Używaj strategii polimorfizmu zamiast switch/if-else

// PRZED: Długi łańcuch if-else
function handleDinosaurBehavior(dinosaur, behavior) {
  if (behavior === 'aggressive') {
    activateDefenses();
    alertSecurity();
    evacuateVisitors();
  } else if (behavior === 'scared') {
    reduceNoiseLevel();
    increaseDistance();
  } else if (behavior === 'hungry') {
    prepareFoodration();
    scheduleFeedingTime();
  } else if (behavior === 'sleeping') {
    reduceActivity();
    dimLights();
  } else if (behavior === 'playful') {
    allowInteraction();
    monitorClosely();
  } else {
    logUnknownBehavior(behavior);
  }
}

// PO: Strategia z mapą funkcji
const behaviorHandlers = {
  aggressive: () => {
    activateDefenses();
    alertSecurity();
    evacuateVisitors();
  },
  scared: () => {
    reduceNoiseLevel();
    increaseDistance();
  },
  hungry: () => {
    prepareFoodRation();
    scheduleFeedingTime();
  },
  sleeping: () => {
    reduceActivity();
    dimLights();
  },
  playful: () => {
    allowInteraction();
    monitorClosely();
  }
};

function handleDinosaurBehavior(dinosaur, behavior) {
  const handler = behaviorHandlers[behavior];

  if (!handler) {
    logUnknownBehavior(behavior);
    return;
  }

  handler();
}

Podsumowanie

"Życie... znajduje drogę," mówi Dr. Malcolm. "Ale kod nie powinien być labiryntem. Każda funkcja powinna mieć jasną, liniową ścieżkę - od walidacji na początku do logiki biznesowej na końcu."

Kluczowe zasady:

  1. Sprawdzaj błędy najpierw - używaj early returns dla edge cases
  2. Neguj warunki - zamiast if (x) użyj if (!x) return
  3. Unikaj zagnieżdżeń - maksymalnie 2-3 poziomy
  4. Wydzielaj logikę - złożone warunki → pomocnicze funkcje
  5. Nazywaj warunki - przypisuj złożone wyrażenia do zmiennych
  6. Używaj nowoczesnego JS - optional chaining, nullish coalescing

Kod z early returns jest:

  • Czytelniejszy - łatwiej zrozumieć flow
  • Łatwiejszy w utrzymaniu - łatwo dodać nowy warunek
  • Mniej podatny na błędy - mniej miejsc gdzie coś może pójść nie tak
  • Bardziej testowalny - każdy warunek można przetestować osobno

Pamiętaj: dobry kod to nie tylko kod, który działa - to kod, który jest łatwy do zrozumienia i modyfikacji. A w Parku Jurajskim, gdzie każdy błąd może mieć katastrofalne skutki, czytelność kodu może uratować życie!

Kod do tej lekcji: index.ts
// Unikanie glebokiego zagniezdzania - Early Returns
// Park Jurajski: System bezpieczenstwa z guard clauses
console.log("=== EARLY RETURNS - PARK JURAJSKI ===\n");

// ===========================================
// 1. Przyklad: Sprawdzanie bezpieczenstwa dinozaura
// ===========================================
console.log("=== 1. SPRAWDZANIE BEZPIECZENSTWA ===");

interface DinosaurData {
  name: string;
  isAlive: boolean;
  health: number;
  aggressionLevel: number;
  enclosure?: {
    fence?: {
      isActive: boolean;
      voltage: number;
    };
  };
  lastFed?: number;
}

// Early returns - kazdy warunek sprawdzany osobno
function checkSafety(dino: DinosaurData): string {
  if (!dino) {
    return "BLAD: Brak danych o dinozaurze";
  }

  if (!dino.isAlive) {
    return "Dinozaur martwy - brak zagrozenia";
  }

  if (!dino.enclosure?.fence) {
    return "ALARM: Brak ogrodzenia!";
  }

  if (!dino.enclosure.fence.isActive) {
    return "ALARM: Ogrodzenie nieaktywne!";
  }

  if (dino.health <= 30) {
    return "OSTRZEZENIE: Niski poziom zdrowia";
  }

  if (dino.aggressionLevel >= 8) {
    return "ALARM: Bardzo wysoki poziom agresji!";
  }

  return "Status: Bezpieczny";
}

const trex: DinosaurData = {
  name: "T-Rex",
  isAlive: true,
  health: 85,
  aggressionLevel: 6,
  enclosure: { fence: { isActive: true, voltage: 10000 } },
  lastFed: Date.now() - 3600000
};

const brokenFence: DinosaurData = {
  name: "Velociraptor",
  isAlive: true,
  health: 90,
  aggressionLevel: 9,
  enclosure: { fence: { isActive: false, voltage: 0 } }
};

console.log(`${trex.name}: ${checkSafety(trex)}`);
console.log(`${brokenFence.name}: ${checkSafety(brokenFence)}`);

// ===========================================
// 2. Walidacja z early returns
// ===========================================
console.log("\n=== 2. WALIDACJA FORMULARZA ===");

interface VisitorForm {
  firstName?: string;
  lastName?: string;
  age?: number;
  email?: string;
  acceptedTerms?: boolean;
}

function validateVisitor(form: VisitorForm): { valid: boolean; error?: string } {
  if (!form.firstName) {
    return { valid: false, error: "Brak imienia" };
  }

  if (!form.lastName) {
    return { valid: false, error: "Brak nazwiska" };
  }

  if (!form.age || form.age < 5) {
    return { valid: false, error: "Minimalny wiek: 5 lat" };
  }

  if (!form.email || !form.email.includes("@")) {
    return { valid: false, error: "Nieprawidlowy email" };
  }

  if (!form.acceptedTerms) {
    return { valid: false, error: "Wymagana akceptacja regulaminu" };
  }

  return { valid: true };
}

const goodForm: VisitorForm = {
  firstName: "Jan",
  lastName: "Kowalski",
  age: 25,
  email: "jan@park.com",
  acceptedTerms: true
};

const badForm: VisitorForm = {
  firstName: "Anna",
  age: 3
};

console.log("Poprawny formularz:", validateVisitor(goodForm));
console.log("Niepoprawny formularz:", validateVisitor(badForm));

// ===========================================
// 3. TODO: Sprobuj sam!
// ===========================================
console.log("\n=== 3. TWOJA KOLEJ ===");
console.log("Refaktoryzuj ponizszy kod uzywajac early returns:");

// TODO: Przepisz te funkcje uzywajac early returns
// zamiast zagniezdzonych if-else

function processOrder(items: string[], payment: number) {
  // Uzyj early returns zamiast zagniezdzonych warunkow!
  if (items && items.length > 0) {
    if (payment > 0) {
      if (payment >= items.length * 10) {
        console.log("Zamowienie przetworzone!");
        return true;
      } else {
        console.log("Za mala kwota");
        return false;
      }
    } else {
      console.log("Brak platnosci");
      return false;
    }
  } else {
    console.log("Brak produktow");
    return false;
  }
}

processOrder(["Bilet VIP", "Lunch"], 50);
processOrder([], 10);
processOrder(["Bilet"], 5);

console.log("\nWskazowka: Uzyj negacji warunkow i return na poczatku!");

Hasła z dokumentacji w tej lekcji

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Czym jest tzw. „Arrow Anti-Pattern” (Pyramid of Doom) w kodzie?

  2. 2. Co zwróci wyrażenie dino?.enclosure?.location ?? null, gdy dino jest undefined?

Zadania praktyczne w grze

  • Układanie w pionie

    Uporządkuj kolejność elementów w funkcji stosującej early returns (od góry):

  • Klikanie w kolejności

    Ułóż elementy klauzuli strażniczej (guard clause):

Przydatne artykuły