Что такое перегрузка функций (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[]), а это менее удобно при работе с конкретным вызовом.

Правила объявления перегрузок

  1. Все перегруженные сигнатуры объявляются до реализации функции.

  2. Реализация функции должна охватывать все варианты, перечисленные в сигнатурах.

  3. Реализация всегда использует более широкий тип (обычно union) для аргументов, чтобы охватить все сценарии.

  4. Перегрузки видны только на этапе компиляции, в скомпилированном 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 будет правильно подсвечивать результат.