programowanieProgramista Typescript

Jak działa typowanie funkcji z przeciążeniem (overload) w TypeScript i jaka jest jego różnica w porównaniu do przeciążenia metod w innych językach?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania

W językach takich jak Java czy C#, przeciążenie funkcji i metod to standardowa możliwość: można zadeklarować kilka funkcji o tej samej nazwie, ale z różnymi parametrami. W JavaScript to nie istnieje, co dodaje potencjalne błędy. TypeScript rozwiązuje ten problem na poziomie typów.

Problem

W TypeScript nie ma prawdziwego przeciążenia (wielokrotnych realizacji). Można zadeklarować kilka sygnatur jednej funkcji, ale zrealizować tylko jedną funkcję. Błędy pojawiają się przy desynchronizacji zadeklarowanych sygnatur i realizacji, lub przy nieprawidłowym pozycjonowaniu sygnatur.

Rozwiązanie

TypeScript wspiera przeciążenie funkcji poprzez zadeklarowanie kilku sygnatur po sobie i jednej rzeczywistej realizacji, kompatybilnej ze wszystkimi wariantami przeciążenia.

Przykład kodu:

function sum(a: number, b: number): number; function sum(a: string, b: string): string; function sum(a: any, b: any): any { return a + b; } const a = sum(1, 2); // 3 const b = sum('foo', 'bar'); // "foobar"

Kluczowe cechy:

  • Zawsze jedna realizacja po wszystkich sygnaturach
  • Realizacja musi być kompatybilna ze wszystkimi wariantami przeciążenia
  • TypeScript sprawdza poprawność wywołania przy użyciu przeciążonej funkcji

Pytania z podstępem.

Czy można zdefiniować realizację funkcji przed zadeklarowaniem wszystkich sygnatur przeciążenia?

Nie, najpierw muszą być wszystkie sygnatury przeciążenia, a potem realizacja. Błąd:

// Błąd! Najpierw sygnatury, potem realizacja function foo(a: number): number { return a } // błąd function foo(a: string): string { return a } // błąd

Czy TypeScript sprawdza wartości zwracane w wszystkich sygnaturach przeciążenia?

TypeScript zapewnia zwrot typów zgodnie z zadeklarowanymi sygnaturami tylko na etapie kompilacji; żadna kontrola typów w czasie wykonywania nie zachodzi.

Czy obowiązkowe jest pokrycie wszystkich wariantów danych wejściowych w jednej realizacji?

Funkcja-realizacja powinna poprawnie obsługiwać wszystkie warianty danych wejściowych zgodnie z sygnaturami przeciążenia:

function parse(a: number): string; function parse(a: string): string; function parse(a: number | string): string { return String(a); }

Typowe błędy i anty-wzorce

  • Niezgodność realizacji i sygnatur przeciążeń
  • Nieprawidłowa kolejność deklaracji sygnatur i realizacji
  • Niejawne przetwarzanie/ignorowanie kombinacji typów

Przykład z życia

Negatywny przypadek

Zadeklarowano kilka przeciążeń funkcji z różnymi typami wejść, ale realizacja obsługuje nie wszystkie przypadki i rzuca błąd w czasie wykonywania przy rzadkich danych wejściowych.

Zalety:

  • Elastyczne API dla użytkowników funkcji

Wady:

  • Typy się kompilują, ale działanie funkcji jest niepoprawne bez dodatkowych sprawdzeń w ciele

Pozytywny przypadek

Każde przeciążenie funkcji jest pokryte warunkiem na typy w realizacji, używane są sprawdzenia typu (typeof/instanceof).

Zalety:

  • API jest bezpieczne i przewidywalne
  • Błędy łapane na etapie kompilacji

Wady:

  • Zwiększa bazę kodu z powodu dodatkowych sprawdzeń