Нужно ли добавлять Promise к выходному типу асинхронной функции в TypeScript
Что проверяют
Знаете ли вы, что TypeScript выводит Promise у асинхронной функции сам, и понимаете ли, зачем тогда писать тип руками. Ответ «надо всегда, иначе будут ошибки» показывает, что человек повторяет правило, не понимая механики.
Как отвечать
Технически указывать тип не обязательно: функция с async всегда возвращает промис, и вывод типов справится без подсказки. Более того, объявить у такой функции не промисный тип нельзя, компилятор потребует Promise или совместимый тип, так что ошибиться здесь трудно.
Писать явно стоит на границах: экспортируемые функции модуля, слой сервисов, публичный интерфейс пакета. Явная аннотация ловит момент, когда реализация тихо начала возвращать не то, и ошибка всплывает в самой функции, а не через три файла у вызывающего.
export async function getUser(id: string): Promise<User> {
const row = await repo.findById(id);
if (!row) throw new NotFoundError(id);
return row;
}Добавьте два уточнения, они отличают знающего человека. У обычной, не асинхронной функции, которая возвращает промис, тип надо написать самому, вывод здесь ничего не подарит. А функция без результата типизируется как Promise<void>, а не void: void сообщает вызывающему, что ждать нечего, и его перестанут дожидаться.
Где спотыкаются
Оборачивают тип второй раз и пишут Promise<Promise<User>> или возвращают Promise.resolve внутри async. Лишняя обёртка не ломает выполнение, промисы схлопываются, но выдаёт непонимание.
Второе: теряют отказ промиса в колбэке с типом void. Асинхронный обработчик маршрута в Express без обёртки не передаёт ошибку дальше, она уходит в необработанный отказ, а процесс Node падает целиком.
Третье: пропускают any из разбора JSON или ответа HTTP-клиента. Тип Promise<any> растекается по всей цепочке вызовов, и дальше компилятор уже ничего не проверяет, хотя аннотации везде расставлены.