Перейти к содержимому
Теория · Простые и особые типы · урок 7 из 8

void и never

Вызов функции похож на обмен посылками. Вы отправляете аргументы, а функция присылает ответную посылку — результат. Тип результата пишут после скобок: ): number обещает, что в ответе придёт число.

Бывают функции, которым нечего прислать в ответ. Одна выводит строку на экран и заканчивает работу: ответная посылка приходит пустой. Другая всегда бросает ошибку: ответная посылка не придёт никогда. Для этих двух случаев в TypeScript есть типы void и never.

void: ответ пустой

Тип void в результате функции значит «полезного результата нет». Функция делает свою работу и ничего не возвращает:

TypeScript
function logLine(text: string): void {
    console.log("> " + text);
}
const result = logLine("start"); // > start
console.log(result); // undefined

Разберём пример по строкам:

  1. logLine объявлена с типом результата void.
  2. Вызов logLine("start") печатает > start.
  3. В result попадает то, что вернула функция. В ней нет return, поэтому JavaScript возвращает undefined.
  4. console.log(result) печатает undefined.

Пустая посылка всё-таки приходит, и внутри неё undefined. Тип void говорит не «здесь undefined», а «результатом не пользуйтесь». Подсказка редактора покажет у result тип void.

Вы уже встречали void, даже если не замечали его. Наведите курсор на log в console.log: в подсказке после скобок стоит : void. Метод печатает текст, а полезного результата не возвращает.

Аннотацию void можно не писать: если в функции нет return со значением, TypeScript сам выведет void. Зато с аннотацией tsc проверяет, что функция и правда ничего не возвращает:

TypeScript
function logLine(text: string): void {
    console.log("> " + text);
    return text.length; // ✗
}
TEXT
error TS2322: Type 'number' is not assignable to type 'void'.

never: ответа не будет

Тип never значит «функция никогда не завершится нормально». Чаще всего такая функция всегда бросает ошибку:

TypeScript
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

Разберём пример:

  1. fail бросает Error и никогда не доходит до конца тела, поэтому её результат — never.
  2. toPercent обещает number, а в одной ветке возвращает fail(...). tsc это пропускает: значение типа never никогда не появится, поэтому оно подходит к любому типу.
  3. Вызов toPercent(1, 4) идёт по второй ветке и печатает 25.

Тип never подходит и функции с бесконечным циклом while (true) без break: она тоже не вернётся. Но сам TypeScript для объявления такой функции или функции, которая только бросает ошибку, выводит void, а не never. Поэтому never пишут явно.

Здесь аналогия с посылками перестаёт работать. Ответная посылка от fail не придёт, но брошенная ошибка не пропадает: её можно поймать в try/catch выше по цепочке вызовов.

Этот вопрос часто задают на собеседовании: «Чем void отличается от never?»

  • void — функция завершается, но полезного результата не возвращает, а вызывающий код получает undefined;
  • never — функция не завершается вовсе: бросает ошибку или работает бесконечно.

Частая ошибка

never написали в значении «ничего не возвращает»:

TypeScript
function logLine(text: string): never { // ✗
    console.log(text);
}
TEXT
error TS2534: A function returning 'never' cannot have a reachable end point.

Сообщение говорит: функция с типом never не должна доходить до конца тела. А logLine доходит: она печатает строку и завершается. Такой функции нужен void.

Главное

  • void в результате значит «полезного результата нет»: функция завершается, а вызывающий код получает undefined.
  • С аннотацией void компилятор не даст вернуть из функции значение.
  • never значит, что функция не завершается нормально: всегда бросает ошибку или работает бесконечно.
  • Функции, которая доходит до конца тела, never писать нельзя: tsc ответит ошибкой TS2534.