Parametry opcjonalne, domyślne i rest
Nie każdy gracz ma pseudonim, nie każde zamówienie ma rabat, a lista punktów może być dowolnie długa. Naucz funkcję obsługiwać takie sytuacje bez zgadywania.
Po tej lekcji potrafisz
- Dobierzesz parametr opcjonalny lub wartość domyślną.
- Zbierzesz argumenty parametrem rest i rozwiniesz tablicę przez spread.
- Odczytasz proste przeciążenie funkcji.
Jedna funkcja, różne zamówienia
Kolega chce kupić jeden bilet, ktoś inny pięć. Większość zamówień nie ma rabatu. Nie potrzebujesz osobnej funkcji dla każdej sytuacji — opisz, co musi zostać podane, a co program może uzupełnić.
Małe kroki, konkretne efekty
Domyślna wartość jest gotowym zastępstwem
Parametr z wartością domyślną może zostać pominięty. Zastępstwo działa też dla jawnego undefined, czyli braku wartości. Nie zastępuje zera, pustego tekstu ani null.
function bilety(ile: number, cena = 20): number {
return ile * cena;
}
console.log(bilety(2));
console.log(bilety(2, 0));
console.log(bilety(2, undefined));
40
0
40
Zero jest prawidłową ceną, więc darmowe bilety pozostają darmowe. Parametry wymagane najczytelniej umieszczać przed opcjonalnymi.
Pytajnik oznacza możliwość braku
Przy parametrze opcjonalnym musisz sam zdecydować, jak obsłużyć brak. W środku funkcji pseudonim ma typ string | undefined: tekst albo brak. Pionową kreskę omówimy szerzej w lekcji 15.
function powitaj(imie: string, pseudonim?: string): string {
if (pseudonim === undefined) { return "Cześć, " + imie; }
return "Cześć, " + pseudonim;
}
console.log(powitaj("Ala"));
console.log(powitaj("Ala", "Pixel"));
Cześć, Ala
Cześć, Pixel
Nie wywołuj metod tekstu przed sprawdzeniem undefined. Zapis ? nie dostarcza automatycznie domyślnego pseudonimu.
Rest zbiera, spread rozpakowuje
Trzy kropki w deklaracji zbierają pozostałe argumenty do tablicy. Przy wywołaniu rozwijają elementy tablicy na osobne argumenty. Parametr rest jest ostatni i w tej funkcji ma typ number[].
function suma(...punkty: number[]): number {
let wynik = 0;
for (const p of punkty) { wynik += p; }
return wynik;
}
const runda = [2, 3, 5];
console.log(suma(), suma(10, 20), suma(...runda));
0 30 10
Puste wywołanie tworzy pustą tablicę, więc wynik wynosi 0. To inny zapis wejścia niż pojedynczy parametr punkty: number[].
Koszt wspólnego zamówienia pizzy
Ceny liczymy w całych złotych. Dostawa domyślnie kosztuje 8 zł; każda kolejna cena pizzy trafia do rest. Przyjmujemy nieujemne ceny.
function zamowienie(dostawa = 8, ...ceny: number[]): number {
let razem = dostawa;
for (const cena of ceny) { razem += cena; }
return razem;
}
const pizze = [30, 35, 25];
console.log("Z dostawą: " + zamowienie(undefined, ...pizze));
console.log("Odbiór własny: " + zamowienie(0, ...pizze));
Z dostawą: 98
Odbiór własny: 90
Jak program dochodzi do wyniku
- undefined uruchamia domyślną dostawę 8 zł.
- Spread rozwija pizze na trzy argumenty. Rest w funkcji zbiera je do lokalnej tablicy ceny.
- Pętla dodaje 30 + 35 + 25 do opłaty za dostawę.
- Zero w drugim wywołaniu świadomie wyłącza opłatę.
Zanim przejdziesz dalej
Ile zwróci zamowienie()? Czy to znaczy, że sklep powinien przyjąć takie zamówienie?
Sprawdź swoje rozumowanie
Zwróci 8. Funkcja zgodnie z kodem nalicza dostawę także bez pizzy. Regułę zakazującą pustych zamówień trzeba dopisać — typy nie ustalają zasad sklepu.
Zmień kod i zobacz, co się stanie
Przeciążenia — różne umowy, jedno ciało
To rozszerzenie dla chętnych. Sygnatury przeciążenia opisują dozwolone wywołania. Niżej zapisujemy jedną implementację obsługującą wszystkie przypadki. typeof sprawdza rodzaj wartości podczas działania.
function etykieta(wartosc: number): string;
function etykieta(wartosc: string): string;
function etykieta(wartosc: number | string): string {
if (typeof wartosc === "number") { return "#" + wartosc; }
return wartosc.toUpperCase();
}
console.log(etykieta(7), etykieta("vip"));
#7 VIP
W tym prostym przypadku jeden parametr number | string byłby wystarczający. Przeciążenia przydają się bardziej, gdy rodzaj wejścia wpływa na typ wyniku. Nie twórz kilku funkcji o tej samej nazwie z osobnymi ciałami.
Gdy wynik zaskakuje
| Pomyłka | Co się dzieje i jak to poprawić |
|---|---|
| Rabat 0 zastąpiony ceną domyślną | Parametr domyślny nie zastępuje zera. Jeśli tak działa Twój program, sprawdź użycie operatora ||. |
| Rest przed innym parametrem | Rest musi być ostatni: inaczej nie wiadomo, które argumenty miałby zebrać. |
| Parametr z ? użyty jak pewny tekst | Sprawdź undefined albo wybierz wartość domyślną. |
undefined na zapowiedź
Parametr opcjonalny, którego nie podano, ma wartość undefined. Na razie traktuj ją jak
„brak wartości”. Czym różni się od null, jak ją bezpiecznie obsłużyć i co robią
operatory ?. oraz ?? — lekcja 16.
Teraz Twoja kolej
Rozwiązanie jest jedną z możliwości. Zanim je odkryjesz, uruchom własną wersję dla podanych danych i przynajmniej jednego przypadku brzegowego.
Funkcja powitaj ma przyjmować nazwę, domyślnie „Gościu”. Wywołaj ją bez argumentu i z „Pixel”.
Mała podpowiedź
Wartość domyślna pozwala wywnioskować string.
Przykładowe rozwiązanie z wyjaśnieniem
function powitaj(nazwa = "Gościu"): string { return "Hej, " + nazwa; }
console.log(powitaj());
console.log(powitaj("Pixel"));
Hej, Gościu
Hej, Pixel
Pominięty argument jest zastąpiony tekstem, który zapisano w deklaracji.
Zwróć nazwę drużyny. Jeżeli podano miasto, dodaj je w nawiasie. Brak miasta nie może dawać tekstu „undefined”.
Mała podpowiedź
Porównaj opcjonalny parametr z undefined.
Przykładowe rozwiązanie z wyjaśnieniem
function podpis(nazwa: string, miasto?: string): string {
if (miasto === undefined) { return nazwa; }
return nazwa + " (" + miasto + ")";
}
console.log(podpis("Wilki"));
console.log(podpis("Wilki", "Gdańsk"));
Wilki
Wilki (Gdańsk)
Obsługujemy brak osobno. Pusty napis byłby nadal podanym miastem — dodatkowa walidacja to osobna reguła.
Zsumuj dowolną liczbę medali parametrem rest. Sprawdź brak argumentów i tablicę [1, 3, 2] rozwiniętą przy wywołaniu.
Mała podpowiedź
Zacznij sumę od zera.
Przykładowe rozwiązanie z wyjaśnieniem
function medale(...liczby: number[]): number {
let suma = 0;
for (const n of liczby) { suma += n; }
return suma;
}
console.log(medale(), medale(...[1, 3, 2]));
0 6
Spread i rest mogą wystąpić po dwóch stronach tego samego wywołania.
Przyjmij domyślny rabat 10% i dowolnie wiele cen. Zwróć sumę po rabacie. Dla 100 i 50 wynik to 135, z rabatem 0 — 150. Zakładamy rabat 0–100 i nieujemne ceny.
Mała podpowiedź
Sumuj ceny, a potem pomnóż przez 1 minus rabat / 100.
Przykładowe rozwiązanie z wyjaśnieniem
function poRabacie(rabat = 10, ...ceny: number[]): number {
let suma = 0;
for (const cena of ceny) { suma += cena; }
return suma * (1 - rabat / 100);
}
console.log(poRabacie(undefined, 100, 50));
console.log(poRabacie(0, 100, 50));
console.log(poRabacie());
135
150
0
Rabat jest pierwszym argumentem, dlatego przy jego pominięciu używamy undefined. W większej aplikacji wygodniejszy może być obiekt opcji.
Co zabierasz do następnej lekcji
- Domyślna wartość reaguje na undefined, a nie na każdą wartość fałszywą.
- Parametr opcjonalny może nie istnieć — trzeba to obsłużyć.
- Rest tworzy tablicę argumentów; spread rozwija tablicę przy wywołaniu.