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

Параметры: необязательные, по умолчанию, остаточные

tsc не пропускает вызов area(3) у функции с двумя параметрами: аргументов должно быть столько же, сколько параметров. Но в JavaScript много функций, у которых аргумент можно не передавать. greet() здоровается с гостем, а greet("Аня") — с Аней. Как сказать об этом в сигнатуре?

Вернёмся к автомату с чаем. Кроме монетоприёмника, у него есть кнопки, и на табличке про них тоже написано:

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

В TypeScript у каждой из этих строк таблички есть своя запись.

Три вида параметров

Необязательный параметр

Знак ? после имени параметра говорит: аргумент можно не передавать.

TypeScript
function greet(name?: string): string {
    if (name === undefined) {
        return "Привет, гость!";
    }
    return "Привет, " + name.toUpperCase() + "!";
}

console.log(greet()); // Привет, гость!
console.log(greet("Аня")); // Привет, АНЯ!

Разберём, что изменилось:

  1. name?: string — параметр необязательный, поэтому оба вызова, greet() и greet("Аня"), проходят проверку.
  2. Внутри функции у name тип string | undefined: если аргумент не передали, там undefined. Так же читалось необязательное свойство age?: number в пятой теме.
  3. Проверка name === undefined сужает тип, и после неё name — уже string, у которого есть toUpperCase.

Уберите проверку, и tsc остановит вызов метода:

TEXT
error TS18048: 'name' is possibly 'undefined'.

Сообщение значит: «name может быть undefined». Необязательные параметры пишут после обязательных: в сигнатуре (greeting?: string, name: string) tsc сообщит об ошибке TS1016 — обязательный параметр не может идти после необязательного.

Значение по умолчанию

Если без аргумента функция должна взять обычное значение, его пишут прямо в параметре, как в JavaScript:

TypeScript
function greet(name = "гость"): string {
    return "Привет, " + name + "!";
}

console.log(greet()); // Привет, гость!
console.log(greet("Аня")); // Привет, Аня!
console.log(greet(undefined)); // Привет, гость!

Тип параметра TypeScript вывел из значения по умолчанию: name — это string. Внутри функции name всегда строка, проверять его не нужно. А для вызывающего кода параметр необязательный: подсказка над greet покажет function greet(name?: string): string.

Значение по умолчанию подставляется, когда аргумент равен undefined. Неважно, не передали его вовсе или передали undefined явно, как в третьем вызове.

Остаточный параметр

Параметр с тремя точками перед именем — остаточный параметр: он собирает все оставшиеся аргументы в массив.

TypeScript
function sum(...nums: number[]): number {
    let total = 0;
    for (const n of nums) {
        total += n;
    }
    return total;
}

console.log(sum()); // 0
console.log(sum(1, 2, 3)); // 6

У остаточного параметра тип массива: nums: number[]. Число аргументов теперь любое, даже ноль, но каждый должен быть числом. sum(1, "2") даст знакомую ошибку TS2345: строку нельзя передать вместо number. Остаточный параметр бывает только последним.

? или значение по умолчанию

На собеседовании часто спрашивают, чем необязательный параметр отличается от параметра со значением по умолчанию. Оба разрешают не передавать аргумент, а разница — в типе внутри функции:

  • name?: string — внутри функции тип string | undefined, и перед использованием нужна проверка;
  • name = "гость" — внутри функции тип string, и проверка не нужна.

Здесь аналогия с автоматом заканчивается. У настоящего автомата «не нажали кнопку» и «нажали „без сахара“» — разные действия. В JavaScript пропущенный аргумент и явный undefined неразличимы: значение по умолчанию сработает в обоих случаях.

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

Знак ? и значение по умолчанию поставили на один параметр, чтобы подчеркнуть, что он необязательный:

TypeScript
function greet(name?: string = "гость"): string { // ✗
    return "Привет, " + name + "!";
}
TEXT
error TS1015: Parameter cannot have question mark and initializer.

Сообщение значит: «у параметра не может быть одновременно знака вопроса и начального значения». Значение по умолчанию уже делает параметр необязательным, поэтому ? здесь лишний: достаточно name = "гость".

Главное

  • name?: string — необязательный параметр; внутри функции его тип string | undefined, и перед использованием его проверяют.
  • name = "гость" — значение по умолчанию; тип внутри функции string, а подставляется значение, когда аргумент равен undefined.
  • ...nums: number[] собирает оставшиеся аргументы в массив; каждый аргумент проверяется по типу элемента.
  • Необязательные параметры и остаточный параметр идут последними, а ? и значение по умолчанию на одном параметре не совмещают.