Menu

Опциональные параметры TypeScript: default и rest

Как сделать параметр в TypeScript опциональным через ?, задать ему значение по умолчанию или собрать любое число аргументов rest-параметром. Правила порядка, пропущенный аргумент и undefined, объекты опций и опциональные параметры в типах функций.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

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

TypeScript проверяет количество аргументов, поэтому без ? первый вызов дал бы ошибку компиляции: Expected 2 arguments, but got 1. (TS2554).

Опциональный параметр может быть undefined

Раз вызывающий код может его пропустить, тип опционального параметра внутри функции это T | undefined. При strictNullChecks нужно обработать случай undefined, прежде чем использовать значение как T.

Обычные инструменты здесь это опциональная цепочка (?.) и оператор нулевого слияния (??). Когда запасное значение фиксированное, короче параметр со значением по умолчанию.

Значения параметров по умолчанию

Значение по умолчанию делает параметр опциональным для вызывающего кода и даёт ему внутри функции тип без undefined. Тип выводится из значения по умолчанию, поэтому аннотация часто не нужна.

Вызывающий код видит сигнатуру repeat(text: string, times?: number, separator?: string). Значения по умолчанию подчиняются правилу JavaScript: они применяются, когда аргумент равен undefined, пропущен он или передан явно, и не применяются, когда он равен null. Выражение по умолчанию может использовать предыдущие параметры: function range(start: number, end = start + 10).

Правила порядка параметров

ОбъявлениеКомпилируется?Примечания
(a: number, b?: number)ДаОпциональные параметры идут последними
(a?: number, b: number)НетTS1016: A required parameter cannot follow an optional parameter.
(a = 0, b: number)ДаНо чтобы использовать значение по умолчанию, придётся писать f(undefined, 5)
(a: number, ...rest: number[])ДаRest-параметр всегда последний
(a?: number, ...rest: number[])ДаОпциональный перед rest разрешён

Параметр со значением по умолчанию перед обязательным допустим, но неудобен. Для вызывающего кода его тип становится number | undefined, а писать undefined как заглушку никому не нравится. Если нужен гибкий первый параметр, используйте объект опций или перегрузку функций.

Пропуск аргумента и undefined

x?: number и x: number | undefined выглядят похоже и внутри функции имеют одинаковый тип. Для вызывающего кода они различаются: первый можно пропустить, второй нужно передать.

Используйте | undefined для обязательного аргумента, у которого может не быть значения, чтобы каждый вызывающий код задумался об этом. Используйте ?, когда пропуск это обычный вызов. (В сообщении действительно написано «1 arguments»: такова формулировка TypeScript.)

Rest-параметры

Rest-параметр, ...name: T[], собирает любое количество аргументов в массив. Он должен быть последним параметром.

Разворачивание массива в фиксированные параметры проверяется строже. Rest-параметр принимает развёрнутый любой number[], но функция, объявленная как (a: number, b: number), принимает только развёрнутый кортеж, потому что TypeScript должен знать длину:

function point(x: number, y: number) { return { x, y }; }

const list = [3, 4];            // number[]
point(...list);
// error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.

const pair = [3, 4] as const;   // readonly [3, 4]
point(...pair);                 // fine

Rest-параметр может иметь и тип кортежа, который типизирует каждую позицию: ...args: [name: string, age?: number].

Объекты опций

Когда у функции больше двух-трёх опциональных параметров, вызывающий код начинает путаться в позициях. Объект опций со значениями по умолчанию даёт именованные аргументы без привязки к порядку.

= {} в конце делает опциональным весь объект. Без него fetchData("/a") даёт ошибку компиляции (Expected 2 arguments, but got 1., TS2554), а в обычном JavaScript тот же вызов бросил бы TypeError во время выполнения, потому что деструктуризации нужен объект, из которого можно читать.

Опциональные параметры в типах колбэков

В типе функции ? означает «вызывающий этот колбэк код может его не передать». Он не означает «колбэк может его пропустить»: колбэк всегда может игнорировать последние параметры. Поэтому не делайте параметры колбэка опциональными только для того, чтобы обработчики могли принимать меньше аргументов.

// Too loose: every handler must now cope with index being undefined
type Visit = (item: string, index?: number) => void;

// Right: the caller always passes both; handlers may use only item
type VisitStrict = (item: string, index: number) => void;
const log: VisitStrict = (item) => console.log(item);

Часто задаваемые вопросы

Как сделать параметр опциональным в TypeScript?

Поставьте ? после его имени: function greet(name?: string). Вызывающий код может его не передавать, а внутри функции его тип string | undefined, поэтому перед использованием его нужно проверить. Значение по умолчанию, name = "there", тоже делает параметр опциональным и убирает undefined внутри функции.

Может ли опциональный параметр стоять перед обязательным в TypeScript?

С ? нет: (a?: number, b: number) даёт ошибку TS1016, «A required parameter cannot follow an optional parameter.» Параметр со значением по умолчанию может стоять первым, но тогда, чтобы использовать значение по умолчанию, вызывающему коду придётся явно передавать undefined, поэтому на практике опциональные параметры и параметры со значением по умолчанию ставят последними.

Чем x?: number отличается от x: number | undefined?

Внутри функции оба имеют тип number | undefined. Разница в месте вызова: с x?: number аргумент можно пропустить, а с x: number | undefined его нужно передать, даже если значение равно undefined. Пропуск даёт ошибку TS2554.

Применяется ли значение по умолчанию, если передать null?

Нет. JavaScript применяет значение по умолчанию, только когда аргумент равен undefined (пропущен или передан явно). null это значение, поэтому оно сохраняется. К тому же при strictNullChecks TypeScript не принимает null для параметра типа number.

Как передать массив как отдельные аргументы в TypeScript?

Разверните его: fn(...args). Для функции с фиксированными параметрами массив должен иметь тип кортежа, например [number, number], или быть создан через as const; разворачивание number[] даёт ошибку TS2556, потому что его длина неизвестна. Разворачивание в rest-параметр (...values: number[]) работает всегда.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ