Przeciążenia funkcji
Kilka sygnatur jednej funkcji: typ wyniku zależy od tego, z jakimi argumentami ją wywołasz.
Przykład
#function toPoints(value: string): number
function toPoints(values: string[]): number[]
function toPoints(input: string | string[]): number | number[] {
if (Array.isArray(input)) return input.map((item) => Number.parseInt(item, 10))
return Number.parseInt(input, 10)
}
const single = toPoints('120')
// const single: number
const many = toPoints(['40', '75', '90'])
// const many: number[]
console.log(single + 30)
console.log(many)150 [40, 75, 90]
Definicja i zastosowanie
#Przeciążenia opisują jedną funkcję kilkoma sygnaturami. Najpierw wypisujesz same nagłówki, bez ciała, a bezpośrednio pod nimi jedną implementację. Przy wywołaniu kompilator sprawdza nagłówki od góry i wybiera pierwszy, który pasuje do argumentów. Dzięki temu dla napisu funkcja może zwracać liczbę, a dla tablicy napisów tablicę liczb.
Sygnatura implementacji jest ukryta: z zewnątrz widać tylko przeciążenia. Implementacja musi obsłużyć wszystkie warianty, więc zwykle przyjmuje unię typów i sama zawęża ją w środku, np. przez Array.isArray() albo typeof. Jeśli któryś nagłówek do niej nie pasuje, kompilator zgłosi „This overload signature is not compatible with its implementation signature”.
Przeciążać można też metody klas, a w interfejsach wystarczy zapisać kilka sygnatur wywołania. Zanim jednak sięgniesz po przeciążenia, sprawdź, czy nie wystarczy unia, parametr opcjonalny albo generyk. Przeciążenia opłacają się wtedy, gdy typ wyniku zależy od argumentów albo gdy warianty różnią się liczbą i znaczeniem parametrów.
Składnia
#function nazwa(a: TypA): WynikA
function nazwa(a: TypB): WynikB
function nazwa(a: TypA | TypB): WynikA | WynikB {
// implementacja
}Więcej przykładów
#function makeRange(end: number): number[]
function makeRange(start: number, end: number): number[]
function makeRange(a: number, b?: number): number[] {
const [start, end] = b === undefined ? [0, a] : [a, b]
return Array.from({ length: end - start }, (_, i) => start + i)
}
console.log(makeRange(4))
console.log(makeRange(2, 5))
makeRange(1, 2, 3)
// Błąd: Expected 1-2 arguments, but got 3.[0, 1, 2, 3] [2, 3, 4]
toPoints(42)
// Błąd: No overload matches this call.
// Overload 1 of 2, '(value: string): number', gave the following error.
// Argument of type 'number' is not assignable to parameter of type 'string'.
// Overload 2 of 2, '(values: string[]): number[]', gave the following error.
// Argument of type 'number' is not assignable to parameter of type 'string[]'.Dobre praktyki
#- Układaj przeciążenia od najbardziej szczegółowego do najogólniejszego. Kompilator bierze pierwsze pasujące, więc ogólna sygnatura na górze przesłoniłaby pozostałe.
- Argument typu unii, np.
string | string[], nie pasuje do żadnego z przeciążeń z przykładu, bo sygnatura implementacji jest niewidoczna. Jeśli takie wywołania są potrzebne, dodaj osobne przeciążenie przyjmujące unię. - Gdy wszystkie warianty zwracają ten sam typ i różnią się tylko typem parametru, zastąp przeciążenia jedną sygnaturą z unią. Kod będzie krótszy, a podpowiedzi w edytorze prostsze.
Powiązane hasła
#- Typy funkcjiTypowanie parametrów, wartości zwracanej i samych funkcji jako wartości.
- Unia (A | B)Typ, który może być jednym z kilku typów.
- Zawężanie typówUstalanie dokładniejszego typu wartości na podstawie sprawdzeń w kodzie.
- Generyki <T>Typy i funkcje z parametrem typu, działające dla wielu typów bez utraty precyzji.
- Typy warunkoweTypy wybierane na podstawie warunku: T extends U ? X : Y.
Widzisz błąd albo brakuje przykładu? Napisz do nas.