История вопроса:
Callback-функции — распространённая практика в JS и TS для асинхронного программирования и делегирования ответственности. В TypeScript типизация таких функций — важная часть обеспечения безопасности, особенно если параметры могут быть опциональными или иметь дефолтные значения.
Проблема:
В динамическом JS отсутствие типизации callback-аргументов приводит к ошибкам передачи значения, путанице с undefined и неправильному order параметров. В TypeScript типизация требуется для избежания подобных проблем, но снаружи сложно соблюсти все нюансы, связанные с опциональностью, порядком и дефолтными значениями.
Решение:
Явно указывать типы всех параметров, опциональные параметры обозначать вопросительным знаком, а значения по умолчанию — указывать прямо при объявлении функции, и не забывать корректно описывать их в типе.
Пример кода:
function fetchData( url: string, callback: (data: any, error?: Error) => void ) { // ... } // Callback с опциональным параметром error fetchData('/api', (data, error) => { if (error) { // обработка } else { // success } }); // Callback с параметром по умолчанию function process( cb: (x: number, y?: number) => void = (x, y = 10) => { /* ... */ } ) { /* ... */ }
Ключевые особенности:
Обязан ли потребитель callback явно учитывать все параметры, включая опциональные?
Нет, он может опускать опциональные параметры, и TypeScript не выдаст ошибку — обработка происходит корректно благодаря синтаксису вопросительного знака.
Можно ли сделать первый параметр в callback опциональным, а второй — обязательным?
Нет. Опциональные параметры всегда должны идти в конце списка аргументов. Нарушение порядка приведёт к ошибке типизации.
Что произойдёт, если не обозначить опциональность параметра, но не передать его в месте вызова?
TypeScript выдаст ошибку — если параметр обязательный, он должен быть передан. Только опциональные или с дефолтом могут быть опущены.
Обозначили второй аргумент callback обязательным, а в вызове не передали его. Получили ошибку компиляции.
Плюсы:
Минусы:
Типизировали callback с опциональным параметром:
(cb: (x: number, y?: number) => void)
либо задали дефолт:
f = (x: number, y = 10) => { ... }
Плюсы:
Минусы: