Что такое перегрузка функций (function overloading) в TypeScript?
В TypeScript перегрузка функций используется для того, чтобы одна и та же функция могла обрабатывать разные типы аргументов и при этом иметь чётко определённые сигнатуры для каждого варианта использования. Это даёт возможность описывать более гибкие интерфейсы и одновременно сохранять строгую типизацию, что особенно важно при работе с большими проектами и командами.
Основная идея перегрузки функций
Перегрузка функций позволяет разработчику описать несколько сигнатур вызова одной функции, где каждая сигнатура определяет возможные типы аргументов и возвращаемое значение. При этом фактическая реализация функции остаётся одна, но TypeScript знает, какие варианты вызова допустимы.
Например:
function getLength(value: string): number;
function getLength(value: any\[\]): number;
function getLength(value: string | any\[\]): number {
return value.length;
}
const len1 = getLength("hello"); // 5
const len2 = getLength(\[1, 2, 3\]); // 3
Здесь определены две сигнатуры перегрузки: одна для строк, другая для массивов. Реализация одна, но IDE и компилятор будут понимать, какие типы допустимы в конкретном вызове.
Отличие перегрузки от объединений типов
Можно подумать, что вместо перегрузки всегда можно использовать union-типы, но перегрузка даёт больше контроля над возвращаемым значением.
Пример с union:
function getLength(value: string | any\[\]): number {
return value.length;
}
Разница в том, что при union компилятор будет знать только об объединённом типе аргумента и одного типа результата. С перегрузкой же можно задать разные типы возврата в зависимости от типа аргумента.
Пример:
function reverse(value: string): string;
function reverse(value: number\[\]): number\[\];
function reverse(value: string | number\[\]): string | number\[\] {
return typeof value === "string"
? value.split("").reverse().join("")
: value.slice().reverse();
}
const str = reverse("hello"); // string
const arr = reverse(\[1, 2, 3\]); // number\[\]
Без перегрузки здесь пришлось бы использовать union-тип для результата (string | number[]), а это менее удобно при работе с конкретным вызовом.
Правила объявления перегрузок
-
Все перегруженные сигнатуры объявляются до реализации функции.
-
Реализация функции должна охватывать все варианты, перечисленные в сигнатурах.
-
Реализация всегда использует более широкий тип (обычно union) для аргументов, чтобы охватить все сценарии.
-
Перегрузки видны только на этапе компиляции, в скомпилированном JavaScript они не сохраняются.
Применение на практике
Перегрузки полезны в случаях, когда одна функция должна работать с разными структурами данных и при этом возвращать значения разных типов в зависимости от аргументов. Основные сценарии:
-
работа с коллекциями (например, поиск по строке или массиву);
-
API для библиотек, где одна и та же функция должна быть универсальной;
-
обработка различных форматов данных (например, даты в виде строки или числа).
Пример:
function parseInput(value: string): Date;
function parseInput(value: number): Date;
function parseInput(value: string | number): Date {
return typeof value === "string" ? new Date(value) : new Date(value);
}
const date1 = parseInput("2023-01-01"); // Date
const date2 = parseInput(1672531200000); // Date
Здесь разработчик получает чёткую подсказку от TypeScript в зависимости от типа аргумента, и IDE будет правильно подсвечивать результат.