Перейти к содержанию

Never

Youtube: Видеоурок о типе never

Egghead: Видеоурок о типе never

При разработке языка программирования используется понятие bottom типа, который является естественным результатом анализа потока кода. TypeScript выполняет анализ потока кода (😎), поэтому он должен достоверно представлять то, что никогда не произойдёт.

Тип never используется в TypeScript для обозначения этого типа bottom. Случаи, когда это происходит естественным путем:

  • Функция никогда ничего не вернёт (например, если в теле функции есть while(true){})
  • Функция всегда выбрасывает ошибку (например, в function foo () {throw new Error ('Не реализована')} тип возвращаемого значения функции foo - never)

Конечно, вы можете использовать это описание и сами:

let foo: never; // Okay

Тем не менее, never может быть присвоено только другое never. Например:

let foo: never = 123; // Ошибка: Тип number нельзя присвоить типу never

// Okay, так как тип возвращаемого значения функции - `never`
let bar: never = (() => {
    throw new Error(
        'Поднимаю руки вверх, будто мне все равно'
    );
})();

Отлично. Теперь давайте перейдем к основному варианту использования :)

Пример использования: тщательные проверки

Вы можете вызывать функции never в never-обстоятельствах.

function foo(x: string | number): boolean {
    if (typeof x === 'string') {
        return true;
    } else if (typeof x === 'number') {
        return false;
    }

    // Без типа never мы бы ошиблись:
    // - Не все пути кода возвращают значение (строгие проверки на null)
    // - Или обнаружен недостижимый код
    // Но поскольку TypeScript понимает, что функция `fail` возвращает `never`
    // Он может позволить вам вызвать её, поскольку вы могли бы использовать её
    // для безопасности выполнения / тщательных проверок.
    return fail('Нетщательный!');
}

function fail(message: string): never {
    throw new Error(message);
}

А поскольку never назначается только другому never, вы также можете использовать его для тщательных проверок во время компиляции. Это описано в разделе размеченные объединения.

Путаница с void

Как только кто-то говорит вам, что возвращается never, когда функция никогда не завершается корректно и ничего не будет возвращено, вы интуитивно хотите думать об этом как о void. Однако void - это значение. "Never" - ложное утверждение в логике.

Функция, которая ничего не возвращает, возвращает значение void. Однако функция которая никогда ничего не возвращает (или всегда выбрасывает ошибку), возвращает never. void - это то, что может быть присвоено (без strictNullChecking), но never никогда не может быть присвоено чему-либо, кроме never.

Логический вывод типа в функциях, которые никогда ничего не возвращают

Для объявлений функций TypeScript по умолчанию подразумевает void, как показано ниже:

// Предполагаемый тип возвращаемого значения: void
function failDeclaration(message: string) {
    throw new Error(message);
}
// Предполагаемый тип возвращаемого значения: never
const failExpression = function (message: string) {
    throw new Error(message);
};

Конечно, вы можете исправить это подробным описанием:

function failDeclaration(message: string): never {
    throw new Error(message);
}

Основная причина - обратная совместимость с реальным кодом JavaScript:

class Base {
    overrideMe() {
        throw new Error('Ты забыл переопределить меня!');
    }
}
class Derived extends Base {
    overrideMe() {
        // Код, который на самом деле возвращается сюда
    }
}

Если Base.overrideMe.

Реальный TypeScript может преодолеть это с помощью abstract функций, но этот логический вывод поддерживается для совместимости.