Menu

Parametry opcjonalne w TypeScript: wartości domyślne i rest

Oznacz parametr w TypeScript jako opcjonalny przez ?, nadaj mu wartość domyślną albo zbierz dowolną liczbę argumentów parametrem rest. Omawiamy zasady kolejności, pominięcie a undefined, obiekty opcji i parametry opcjonalne w typach funkcji.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Dodaj ? po nazwie parametru, żeby był opcjonalny. Wywołujący mogą go pominąć, a wewnątrz funkcji jego typ zawiera undefined.

TypeScript sprawdza liczbę argumentów, więc bez ? pierwsze wywołanie byłoby błędem kompilacji: Expected 2 arguments, but got 1. (TS2554).

Parametry opcjonalne mogą być undefined

Ponieważ wywołujący może go pominąć, typ parametru opcjonalnego wewnątrz funkcji to T | undefined. Przy strictNullChecks musisz obsłużyć przypadek undefined, zanim użyjesz go jako T.

Optional chaining (?.) i nullish coalescing (??) to tu typowe narzędzia. Gdy wartość zastępcza jest stała, parametr domyślny jest krótszy.

Domyślne wartości parametrów

Wartość domyślna czyni parametr opcjonalnym dla wywołujących i nadaje mu typ bez undefined wewnątrz funkcji. Typ jest wnioskowany z wartości domyślnej, więc adnotacja często jest zbędna.

Wywołujący widzi sygnaturę repeat(text: string, times?: number, separator?: string). Wartości domyślne działają według reguły JavaScriptu: stosuje się je, gdy argument to undefined, pominięty lub przekazany jawnie, a nie gdy to null. Wyrażenie domyślne może używać wcześniejszych parametrów: function range(start: number, end = start + 10).

Zasady kolejności parametrów

DeklaracjaKompiluje się?Uwagi
(a: number, b?: number)TakParametry opcjonalne idą na końcu
(a?: number, b: number)NieTS1016: A required parameter cannot follow an optional parameter.
(a = 0, b: number)TakAle wywołujący musi napisać f(undefined, 5), żeby użyć wartości domyślnej
(a: number, ...rest: number[])TakParametr rest jest zawsze ostatni
(a?: number, ...rest: number[])TakOpcjonalny przed rest jest dozwolony

Parametr z wartością domyślną przed wymaganym jest legalny, ale niewygodny. Dla wywołujących jego typ staje się number | undefined, a nikt nie lubi pisać undefined jako wypełniacza. Jeśli potrzebujesz elastycznego pierwszego parametru, użyj obiektu opcji albo przeciążania funkcji.

Pominięcie a undefined

x?: number i x: number | undefined wyglądają podobnie i mają ten sam typ wewnątrz funkcji. Różnią się dla wywołującego: pierwszy można pominąć, drugi trzeba przekazać.

Użyj | undefined dla wymaganego argumentu, który może nie mieć wartości, żeby każdy wywołujący musiał o nim pomyśleć. Użyj ?, gdy pominięcie to normalne wywołanie. (Komunikat naprawdę mówi "1 arguments": tak to formułuje TypeScript.)

Parametry rest

Parametr rest, ...name: T[], zbiera dowolną liczbę argumentów w tablicę. Musi być ostatnim parametrem.

Rozkładanie tablicy na stałe parametry jest bardziej rygorystyczne. Parametr rest przyjmuje rozłożenie dowolnej number[], ale funkcja zadeklarowana jako (a: number, b: number) przyjmuje tylko rozłożenie krotki, bo TypeScript musi znać długość:

function point(x: number, y: number) { return { x, y }; }

const list = [3, 4];            // number[]
point(...list);
// error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.

const pair = [3, 4] as const;   // readonly [3, 4]
point(...pair);                 // fine

Parametr rest może też mieć typ krotki, który typuje każdą pozycję: ...args: [name: string, age?: number].

Obiekty opcji

Gdy funkcja ma więcej niż dwa lub trzy parametry opcjonalne, wywołujący gubią się w pozycjach. Obiekt opcji z wartościami domyślnymi daje nazwane argumenty w dowolnej kolejności.

= {} na końcu czyni cały obiekt opcjonalnym. Bez tego fetchData("/a") to błąd kompilacji (Expected 2 arguments, but got 1., TS2554), a w zwykłym JavaScripcie to samo wywołanie rzuciłoby TypeError w czasie działania, bo destrukturyzacja potrzebuje obiektu, z którego czyta.

Parametry opcjonalne w typach callbacków

W typie funkcji ? oznacza "wywołujący ten callback może go nie przekazać". Nie oznacza "callback może go pominąć": callback zawsze może zignorować końcowe parametry. Nie oznaczaj więc parametrów callbacku jako opcjonalnych tylko po to, żeby handlery mogły przyjmować mniej argumentów.

// Too loose: every handler must now cope with index being undefined
type Visit = (item: string, index?: number) => void;

// Right: the caller always passes both; handlers may use only item
type VisitStrict = (item: string, index: number) => void;
const log: VisitStrict = (item) => console.log(item);

Najczęściej zadawane pytania

Jak zrobić parametr opcjonalny w TypeScript?

Postaw ? po jego nazwie: function greet(name?: string). Wywołujący może go pominąć, a wewnątrz funkcji jego typ to string | undefined, więc sprawdzasz go przed użyciem. Nadanie parametrowi wartości domyślnej, name = "there", też czyni go opcjonalnym i usuwa undefined wewnątrz funkcji.

Czy parametr opcjonalny może stać przed wymaganym w TypeScript?

Nie z ?: (a?: number, b: number) to błąd TS1016, "A required parameter cannot follow an optional parameter." Parametr z wartością domyślną może stać pierwszy, ale wtedy wywołujący muszą jawnie przekazać undefined, żeby użyć wartości domyślnej, więc w praktyce parametry opcjonalne i domyślne idą na końcu.

Czym różni się x?: number od x: number | undefined?

Wewnątrz funkcji oba mają typ number | undefined. Różnica jest w miejscu wywołania: przy x?: number argument można pominąć, a przy x: number | undefined trzeba go przekazać, nawet jeśli wartość to undefined. Pominięcie daje błąd TS2554.

Czy przekazanie null używa wartości domyślnej parametru?

Nie. JavaScript stosuje wartość domyślną tylko wtedy, gdy argument to undefined (pominięty lub przekazany jawnie). null to wartość, więc zostaje zachowana. Zresztą przy strictNullChecks TypeScript odrzuca null dla parametru typu number.

Jak przekazać tablicę jako osobne argumenty w TypeScript?

Rozłóż ją: fn(...args). Dla funkcji o stałych parametrach tablica musi mieć typ krotki, taki jak [number, number], albo pochodzić z as const; rozłożenie number[] daje błąd TS2556, bo jej długość jest nieznana. Rozłożenie do parametru rest (...values: number[]) działa zawsze.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ