Przeciążenie funkcji pozwala stworzyć jedną funkcję z różnymi wariantami przyjmowanych parametrów i zwracanych wartości. W innych językach, takich jak C# czy Java, można opisać kilka funkcji o tej samej nazwie, ale z różnymi typami lub liczbą argumentów. TypeScript zbliża ten mechanizm poprzez przeciążenie sygnatur na poziomie typów, ale ciało funkcji jest zawsze jedno.
Historia pytania — w JavaScript nie ma natywnego wsparcia dla przeciążenia według typu lub liczby parametrów. Wraz z pojawieniem się TypeScript przeciążenie jest emulowane poprzez zadeklarowanie kilku sygnatur funkcji z rzędu i jednej realizacji, wewnątrz której ręcznie odróżnia się, z jakim typem aktualnie pracujemy.
Problem pojawia się, gdy nie dba się o zgodność ze stwierdzonymi sygnaturami: parametrom wewnątrz ciała funkcji TypeScript przypisuje najbardziej ogólny typ (połączenie wszystkich typów parametrów) i programista musi przeprowadzać sprawdzenia oraz type guards.
Rozwiązanie: Ściśle typować przeciążenia w jawnych sygnaturach, mądrze korzystać z union types i type guards, poprawnie dokumentować zachowanie.
Przykład kodu:
function format(value: string): string; function format(value: number, locale: string): string; function format(value: any, locale?: string): string { if (typeof value === 'number') { return value.toLocaleString(locale); } return value.trim(); } format(' hello '); // 'hello' format(123456, 'ru-RU'); // '123 456'
Kluczowe cechy:
Czy można zadeklarować kilka funkcji o tej samej nazwie i różnych ciałach, jak w C#?
Nie. W TypeScript (i w JavaScript) faktycznie istnieje tylko jedna funkcja o tej nazwie. Przeciżenia działają tylko na poziomie typów dla kompilatora, ale istnieje tylko jedno ciało funkcji.
Co się stanie, jeśli nie zrealizujesz sygnatury z najbardziej ogólnymi parametrami po przeciążających sygnaturach?
TypeScript zgłosi błąd kompilacji. Zawsze powinna być jedna funkcja-realizacja, której parametry pokrywają wszystkie możliwe warianty przeciżeń.
function foo(a: string): string; function foo(a: number): number; // brak ciała — błąd
Czy mogę wewnątrz ciała używać właściwości specyficznych dla jednej konkretnej sygnatury?
Nie, tylko po sprawdzeniu typu (type guard) lub rzutowaniu typu. W przeciwnym razie TypeScript nie pozwoli używać tych właściwości bezpośrednio, ponieważ nie wiadomo, która sygnatura jest aktualnie wywoływana.
function bar(x: string): number; function bar(x: number): number; function bar(x: string | number): number { if (typeof x === 'string') return x.length; return x * 2; }
Zapomnieli dodać uniwersalną sygnaturę-realizację:
function sum(a: string, b: string): string; function sum(a: number, b: number): number; // brak realizacji — kompilator się skarży
Zalety:
Wady:
Realizują przeciążenie zgodnie z normą:
function sum(a: string, b: string): string; function sum(a: number, b: number): number; function sum(a: any, b: any): any { return typeof a === 'string' && typeof b === 'string' ? a + b : a + b; }
Zalety:
Wady: