void и never
Вызов функции похож на обмен посылками. Вы отправляете аргументы, а функция присылает ответную посылку — результат. Тип результата пишут после скобок: ): number обещает, что в ответе придёт число.
Бывают функции, которым нечего прислать в ответ. Одна выводит строку на экран и заканчивает работу: ответная посылка приходит пустой. Другая всегда бросает ошибку: ответная посылка не придёт никогда. Для этих двух случаев в TypeScript есть типы void и never.
void: ответ пустой
Тип void в результате функции значит «полезного результата нет». Функция делает свою работу и ничего не возвращает:
function logLine(text: string): void {
console.log("> " + text);
}
const result = logLine("start"); // > start
console.log(result); // undefinedРазберём пример по строкам:
logLineобъявлена с типом результатаvoid.- Вызов
logLine("start")печатает> start. - В
resultпопадает то, что вернула функция. В ней нетreturn, поэтому JavaScript возвращаетundefined. console.log(result)печатаетundefined.
Пустая посылка всё-таки приходит, и внутри неё undefined. Тип void говорит не «здесь undefined», а «результатом не пользуйтесь». Подсказка редактора покажет у result тип void.
Вы уже встречали void, даже если не замечали его. Наведите курсор на log в console.log: в подсказке после скобок стоит : void. Метод печатает текст, а полезного результата не возвращает.
Аннотацию void можно не писать: если в функции нет return со значением, TypeScript сам выведет void. Зато с аннотацией tsc проверяет, что функция и правда ничего не возвращает:
function logLine(text: string): void {
console.log("> " + text);
return text.length; // ✗
}error TS2322: Type 'number' is not assignable to type 'void'.never: ответа не будет
Тип never значит «функция никогда не завершится нормально». Чаще всего такая функция всегда бросает ошибку:
function fail(message: string): never {
throw new Error(message);
}
function toPercent(part: number, whole: number): number {
if (whole === 0) {
return fail("whole is zero");
}
return (part / whole) * 100;
}
console.log(toPercent(1, 4)); // 25Разберём пример:
failбросаетErrorи никогда не доходит до конца тела, поэтому её результат —never.toPercentобещаетnumber, а в одной ветке возвращаетfail(...).tscэто пропускает: значение типаneverникогда не появится, поэтому оно подходит к любому типу.- Вызов
toPercent(1, 4)идёт по второй ветке и печатает25.
Тип never подходит и функции с бесконечным циклом while (true) без break: она тоже не вернётся. Но сам TypeScript для объявления такой функции или функции, которая только бросает ошибку, выводит void, а не never. Поэтому never пишут явно.
Здесь аналогия с посылками перестаёт работать. Ответная посылка от fail не придёт, но брошенная ошибка не пропадает: её можно поймать в try/catch выше по цепочке вызовов.
Этот вопрос часто задают на собеседовании: «Чем void отличается от never?»
void— функция завершается, но полезного результата не возвращает, а вызывающий код получаетundefined;never— функция не завершается вовсе: бросает ошибку или работает бесконечно.
Частая ошибка
never написали в значении «ничего не возвращает»:
function logLine(text: string): never { // ✗
console.log(text);
}error TS2534: A function returning 'never' cannot have a reachable end point.Сообщение говорит: функция с типом never не должна доходить до конца тела. А logLine доходит: она печатает строку и завершается. Такой функции нужен void.
Главное
voidв результате значит «полезного результата нет»: функция завершается, а вызывающий код получаетundefined.- С аннотацией
voidкомпилятор не даст вернуть из функции значение. neverзначит, что функция не завершается нормально: всегда бросает ошибку или работает бесконечно.- Функции, которая доходит до конца тела,
neverписать нельзя:tscответит ошибкойTS2534.