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

Параметры и результат

Чему вы научитесь

Чему вы научитесь:

  • находить функцию, которая возвращает обещанный тип не на каждом пути, и вызов с лишним или недостающим аргументом;
  • объявлять необязательные параметры, параметры со значением по умолчанию и остаточные параметры;
  • описывать тип функции и писать колбэки без лишних аннотаций;
  • объявлять перегрузки и понимать, когда вместо них хватит объединения.
Торговый автомат с табличкой number => string принимает монету и выдаёт батончик

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

У функции JavaScript такой таблички нет. Функцию area(width, height) можно вызвать с одним аргументом: второй параметр получит undefined, 3 * undefined даст NaN, и программа пойдёт дальше с этим NaN. Ошибку заметят позже — там, где NaN окажется на экране.

В TypeScript табличку пишет автор функции. Типы параметров и результата вы уже умеете указывать: с них начиналась вторая тема. В этом уроке посмотрим, что tsc проверяет по такой табличке — каждый путь внутри функции и каждый вызов снаружи.

Сигнатура функции

TypeScript
function area(width: number, height: number): number {
    return width * height;
}

console.log(area(3, 4)); // 12

Первая строка и есть табличка автомата. Её называют сигнатурой функции: это имя, параметры с их типами и тип результата. Разберём её по частям:

  • area — имя функции;
  • (width: number, height: number) — два параметра, оба числа: автомат берёт ровно две монеты;
  • : number после скобок — тип результата: функция обещает вернуть число.

По сигнатуре tsc проверяет функцию с двух сторон:

  1. Внутри функции — что каждый путь до конца тела возвращает обещанный тип.
  2. Снаружи — что каждый вызов передаёт столько аргументов, сколько у функции параметров, и нужных типов.

Результат на каждом пути

Функция может закончиться в разных местах: на одном return, на другом или на последней строке тела, если ни один return не встретился. В последнем случае JavaScript возвращает undefined. tsc проходит по всем путям и проверяет, что каждый возвращает тип из сигнатуры:

TypeScript
function grade(score: number): string { // ✗
    if (score >= 50) {
        return "зачёт";
    }
}
TEXT
error TS2366: Function lacks ending return statement and return type does not include 'undefined'.

Сообщение значит: «функции не хватает return в конце, а тип результата не включает undefined». При score меньше 50 условие не выполнится, функция дойдёт до конца тела и вернёт undefined. А сигнатура обещала строку. Поэтому ошибка стоит на типе результата в первой строке: нарушено именно это обещание.

Исправление — вернуть строку и на втором пути, например дописать return "незачёт"; после if.

Без аннотации : string ошибки на функции не было бы. TypeScript вывел бы тип результата "зачёт" | undefined, и undefined всплыл бы там, где результат используют как строку. Аннотация результата переносит проверку в саму функцию — туда, где ошибка и сделана.

Число аргументов

В JavaScript функцию можно вызвать с любым числом аргументов: недостающие получат undefined, лишние потеряются. TypeScript сверяет каждый вызов с сигнатурой:

TypeScript
function area(width: number, height: number): number {
    return width * height;
}

console.log(area(3)); // ✗
TEXT
error TS2554: Expected 2 arguments, but got 1.

Сообщение значит: «ожидалось 2 аргумента, а передан 1». Если бы JavaScript выполнил этот вызов, height получил бы undefined, и area вернула бы NaN. Лишний аргумент tsc тоже не пропустит: area(3, 4, 5) даст ту же ошибку с текстом Expected 2 arguments, but got 3.

Здесь аналогия с автоматом заканчивается. Настоящий автомат проверяет монеты в момент оплаты, а tsc сверяет вызовы с сигнатурой до запуска программы. При запуске JavaScript по-прежнему примет любое число аргументов: проверка была только в tsc.

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

Ветки if покрывают все случаи, которые вы держите в голове, и всё равно tsc сообщает об ошибке:

TypeScript
function sign(n: number): string { // ✗
    if (n > 0) {
        return "плюс";
    } else if (n < 0) {
        return "минус";
    } else if (n === 0) {
        return "ноль";
    }
}
TEXT
error TS2366: Function lacks ending return statement and return type does not include 'undefined'.

Кажется, что число бывает только положительным, отрицательным или нулём. Но tsc не проверяет, покрывают ли условия все числа: после последнего else if для него остаётся путь без return. И этот путь настоящий: у NaN ложны все три сравнения, и sign(NaN) вернул бы undefined. Последнюю проверку заменяют на else или пишут return после всех веток.

Главное

  • Сигнатура функции — имя, параметры с типами и тип результата; по ней tsc проверяет и тело функции, и каждый вызов.
  • Если хотя бы один путь доходит до конца тела без return, функция с типом результата string даёт ошибку TS2366.
  • Вызов с недостающим или лишним аргументом — ошибка TS2554, хотя JavaScript принял бы его молча.
  • Цепочку else if с return в каждой ветке заканчивают веткой else или return после всех веток.