Перейти к содержимому
Теория · Функции · урок 5 из 10

Тип функции

В JavaScript функция — такое же значение, как число или строка. Её кладут в переменную, передают в другую функцию, хранят в массиве. Но какой тип у переменной, в которой лежит функция?

Представьте стену с автоматами на вокзале. Над одним из мест висит табличка «Берёт две монеты, выдаёт стакан», а самого автомата ещё нет. Поставить туда можно любой автомат, который берёт две монеты и выдаёт стакан, — неважно, кто его сделал и что у него внутри. Тип функции — такая табличка без автомата: он описывает, какие параметры функция принимает и что возвращает, а саму функцию подставят позже.

Запись типа функции

Наведите курсор на add в строке const add = (a: number, b: number) => a + b;. Подсказка покажет const add: (a: number, b: number) => number. Часть после двоеточия и есть тип функции. Его можно назвать через type, как любой другой тип:

TypeScript
type Op = (a: number, b: number) => number;

const add: Op = (a: number, b: number) => a + b;
const mul: Op = (x: number, y: number) => x * y;

function apply(op: Op, a: number, b: number): number {
    return op(a, b);
}

console.log(apply(add, 2, 3), apply(mul, 2, 3)); // 5 6

Разберём запись (a: number, b: number) => number по частям:

  • в скобках — параметры с типами, как в сигнатуре функции;
  • после стрелки => — тип результата: в типе функции его пишут после стрелки, а не после двоеточия;
  • имена a и b нужны только читателю: у mul параметры называются x и y, и она всё равно подходит к Op.

Теперь пример по строкам:

  1. type Op даёт имя табличке: «берёт два числа, возвращает число».
  2. add и mul — две разные функции, и обе подходят к Op.
  3. Параметр op: Op в apply принимает любую функцию этого типа.
  4. apply(add, 2, 3) вызывает add и возвращает 5, а apply(mul, 2, 3) — 6.

Функция не того вида

Если параметры или результат функции не совпадают с табличкой, tsc не даст положить её в переменную:

TypeScript
type Op = (a: number, b: number) => number;

const glue: Op = (a: string, b: string) => a + b; // ✗
TEXT
error TS2322: Type '(a: string, b: string) => string' is not assignable to type 'Op'.

Сообщение значит: «функцию типа (a: string, b: string) => string нельзя присвоить типу Op». apply передаст в неё два числа, а функция ждёт строки. В сообщении tsc показывает тип функции в той же записи со стрелкой.

Здесь аналогия с табличкой заканчивается. На табличке автомата не пишут имён для монет. А в типе функции у параметров имена есть, но tsc на них не смотрит: он сравнивает только типы параметров и тип результата.

void в типе функции

В третьей теме функция с аннотацией : void не могла вернуть значение: tsc отвечал ошибкой TS2322. В типе функции void ведёт себя иначе:

TypeScript
type Callback = (n: number) => void;

const saved: number[] = [];
const keep: Callback = (n: number) => saved.push(n);

const r = keep(5);
console.log(r, saved.length); // 1 1

Метод push возвращает новую длину массива, поэтому стрелка возвращает число. И всё же она подходит к Callback, а при запуске keep(5) возвращает 1. Но у r тип void, поэтому r + 1 даст ошибку TS2365: результатом такой функции пользоваться нельзя. Передать r в console.log можно — он принимает значение любого типа, — а считать с ним или вызывать его методы tsc не даст.

void в типе функции значит «результат никто не использует», а не «функция ничего не возвращает». Так удобно писать колбэки: метод forEach ждёт функцию с результатом void, и стрелка (n: number) => saved.push(n) подходит к нему, хотя push что-то возвращает. На собеседовании об этом спрашивают так: «Почему функция, которая возвращает число, подходит к типу () => void?»

Частая ошибка

Тип результата записали через двоеточие, как в объявлении функции:

TypeScript
type Op = (a: number, b: number): number; // ✗
TEXT
error TS1005: '=>' expected.

Сообщение значит: «ожидалась стрелка =>». Двоеточие перед типом результата пишут в объявлении функции, а в типе функции — стрелку: (a: number, b: number) => number.

Главное

  • Тип функции записывают как (a: number, b: number) => number: параметры с типами, стрелка, тип результата.
  • К типу функции подходит любая функция с теми же типами параметров и результата; имена параметров не важны.
  • void в типе функции значит «результат не используют»: функция, которая возвращает значение, тоже подходит.
  • В типе функции тип результата пишут после =>, а не после двоеточия.