Типы функций и перегрузки
Функция как значение: описываем её тип целиком и знакомимся с перегрузками.
Тип функции описывает её сигнатуру — типы параметров и возврата — в форме
(a: number) => string.
Функция — это тоже значение
В JavaScript функции передают в переменные и аргументы. Чтобы типизировать такую переменную, нужен тип функции. Он записывается стрелкой:
let formatter: (value: number) => string;
formatter = (n) => n.toFixed(2); // ок: подходит по сигнатуре
formatter = (n) => n; // Ошибка: возвращает number, а нужен string
Тип (value: number) => string читается как «функция, принимающая число и возвращающая строку». В стрелке параметру дают любое имя — важны только типы.
Зачем это: типизация колбэков
Главная польза — описание колбэков. Когда функция принимает другую функцию, её тип задаёт контракт для колбэка:
function processItems(
items: number[],
callback: (item: number) => void
): void {
for (const item of items) {
callback(item);
}
}
processItems([1, 2, 3], (n) => console.log(n * 10));
TypeScript проверит, что переданный колбэк соответствует сигнатуре: принимает число и не обязан ничего возвращать. Если колбэк ждёт строку — будет ошибка ещё до запуска.
Проверяемый пример
function applyTwice(fn, value) {
return fn(fn(value));
}
const addExclaim = (s) => s + "!";
console.log(applyTwice(addExclaim, "Ура"));
const square = (x) => x * x;
console.log(applyTwice(square, 3));
Вывод:
Ура!! 81
Перегрузки функций
Иногда функция ведёт себя по-разному в зависимости от типов аргументов. Перегрузка позволяет описать несколько вариантов сигнатуры для одной функции. Сначала идут объявления-сигнатуры, затем одна реализация:
// Сигнатуры-перегрузки:
function double(x: number): number;
function double(x: string): string;
// Одна реализация, покрывающая все варианты:
function double(x: number | string): number | string {
if (typeof x === "number") {
return x * 2;
}
return x + x;
}
const a = double(5); // тип результата: number
const b = double("ля"); // тип результата: string
Без перегрузок результат имел бы тип number | string в обоих случаях, и пришлось бы сужать его вручную. Перегрузки дают точный тип для каждого варианта вызова: для числа — number, для строки — string.
Когда нужны перегрузки
Перегрузки — инструмент нечастый. Чаще достаточно union-типа или дженериков (о них в последнем разделе). Перегрузки оправданы, когда связь между типом аргумента и типом результата нельзя выразить иначе — как в примере выше.
Итог
- Тип функции
(a: T) => Rописывает сигнатуру и нужен для переменных и колбэков. - Типизированный колбэк задаёт контракт: компилятор проверит переданную функцию.
- Перегрузки описывают несколько сигнатур одной функции и дают точный тип результата для каждого варианта вызова.