TypeScript / 39 минут
Типизация функций
Опишите параметры, результаты, callbacks и допустимые формы вызова.
- Опишите параметры, результаты, callbacks и допустимые формы вызова.
- Применить тему в задаче «Типизация функций» и объяснить граничные случаи.
Около 9 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.
Сигнатура как договорённость о вызове
Тип функции описывает допустимые параметры и результат, но не обещает конкретный алгоритм внутри. Это позволяет передавать реализацию как зависимость: форматирование, сравнение, получение времени или чтение данных. Хорошая сигнатура сохраняет смысл связи входа и выхода, чтобы потребителю не приходилось использовать any и угадывать результат каждого вызова.
Аннотацию можно записать у объявления функции или отдельно у переменной, хранящей функцию. В контекстно типизированном callback TypeScript часто выводит параметры из ожидаемого типа. Используйте этот вывод, когда он делает код яснее; явное объявление полезно для экспортируемого контракта и сложных случаев совместимости.
type Formatter = (name: string) => string;
const upper: Formatter = name => name.toUpperCase();
function printName(name: string, format: Formatter): string {
return format(name);
}Optional, default и rest
Необязательный параметр может отсутствовать, поэтому внутри функции его тип учитывает undefined. Параметр с начальным значением получает правило замены при отсутствии или undefined. null и пустая строка этим правилом автоматически не заменяются. Не проектируйте длинную цепочку необязательных позиционных параметров, если объект options сделает вызов заметно понятнее.
function greeting(name = 'Гость', prefix?: string): string {
return `${prefix ?? 'Привет'}, ${name}`;
}
function total(...values: number[]): number {
return values.reduce((sum, value) => sum + value, 0);
}Rest-параметр описывается массивом или кортежем подходящей формы и должен быть последним. Он не означает, что все входные данные уже проверены во время исполнения. Сигнатура защищает проверяемые TypeScript-вызовы, но HTTP-клиент может отправить что угодно. Это разделение остаётся актуальным при любом количестве аннотаций.
Возвращаемое значение, void и never
number или string задают конкретный тип результата. void используют для контракта, где вызывающий код не должен опираться на возвращённое значение. Это особенно часто встречается у callback для побочного эффекта. never обозначает отсутствие нормального возврата, например функцию, которая всегда бросает исключение. Эти типы не следует заменять друг другом как синонимы «ничего».
function fail(message: string): never { throw new Error(message); }
function notify(message: string): void { console.log(message); }
async function calculate(): Promise<number> { return 42; }Async-функция возвращает Promise результата. Если забыть дождаться её в вызывающем коде, статическая проверка часто помогает увидеть несоответствие Promise<number> и number. Но отклонение Promise остаётся runtime-сценарием, который нужно обработать соответствующим способом.
Перегрузки и связь вариантов
Перегрузки описывают несколько допустимых форм вызова одной реализации. Они полезны, когда форма результата зависит от формы аргументов и обычный union теряет важную связь. Реализация должна корректно обслуживать все заявленные сигнатуры, но её широкая внутренняя сигнатура не становится дополнительным публичным вариантом сама по себе.
function size(value: string): number;
function size(value: string[]): number;
function size(value: string | string[]): number {
return value.length;
}Не используйте перегрузки там, где простая сигнатура с union полностью выражает контракт. Избыточные формы повышают стоимость изменения и затрудняют чтение сообщений об ошибках. Если важно сохранить тип элемента между несколькими аргументами и результатом, возможно, нужен generic, который подробно разбирается в следующем модуле.
Совместимость callbacks
Callback получает аргументы от вызывающего API, а не от ваших ожиданий. Например, map передаёт элемент, индекс и массив. Функция, переданная напрямую, может интерпретировать дополнительные аргументы иначе, чем вы предполагали. Известный пример — parseInt, которому второй аргумент задаёт систему счисления. Короткая обёртка иногда делает смысл безопаснее и очевиднее.
const values = ['10', '10', '10'];
const numbers = values.map(value => parseInt(value, 10));Также различайте «callback может быть вызван без аргумента» и «callback может проигнорировать переданный аргумент». Не делайте параметр optional только потому, что часть реализаций его не использует. Optional описывает, что разрешено вызывающей стороне, и может добавить ненужный undefined в каждую реализацию.
Практика форматирования
В задаче нужен callback с явным типом (name:string) => string. Он использует prefix из внешнего окружения и применяется к массиву через map. Это соединяет статический контракт функции с уже изученным замыканием JavaScript. TypeScript не меняет принцип создания окружений и не выполняет map за разработчика.
Проверьте пустой массив, пустое имя и префикс на кириллице. Возвращаемый тип solve — string[], поэтому вызов forEach без отдельного результата не подойдёт. После решения вынесите Formatter в отдельный type alias и добавьте две независимые реализации. Потребитель должен работать с обеими через один понятный контракт без принудительных утверждений типов.