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

Null и Undefined

В JavaScript (и, соответственно, TypeScript) есть два нижних типа: null и undefined. Они предназначены для обозначение разных вещей:

  • Что-то еще не инициализированное: undefined.
  • Что-то недоступное в данный момент: null.

Проверяем оба

Факт в том, что вам придется столкнуться с обоими. Просто проверьте их через ==

// Представьте, что вы делаете `foo.bar == undefined`,
// где bar может быть одним из:
console.log(undefined == undefined); // true
console.log(null == undefined); // true

// Вам не нужно переживать из-за ложных значений в этой проверке
console.log(0 == undefined); // false
console.log('' == undefined); // false
console.log(false == undefined); // false

Поручите == null проверить undefined или null. Как правило, нет необходимости делать между ними различия.

function foo(arg: string | null | undefined) {
    if (arg != null) {
        // arg должен быть строкой,
        // поскольку `!=` исключает и null и undefined.
    }
}

Одно исключение: значения undefined корневого уровня, которые мы обсудим далее.

Проверяем undefined корневого уровня

Помните как я сказал вам использовать == null? Не используйте его для вещей корневого уровня. В строгом режиме, если вы используете foo и foo является undefined, вы получите исключение ReferenceError и весь стек вызовов прервётся.

Вы должны использовать строгий режим ... и на самом деле компилятор TS включит его для вас, если вы используете модули ... подробнее об этом будет сказано позже, так что не парьтесь :)

Поэтому чтобы проверить задана ли переменная на глобальном уровне, используйте typeof:

if (typeof someglobal !== 'undefined') {
    // someglobal теперь можно безопасно использовать
    console.log(someglobal);
}

Ограничьте явное использование undefined

Потому что TypeScript дает вам возможность документировать ваши структуры отдельно от значений. Вместо того чтобы делать так:

function foo() {
    // if Something
    return { a: 1, b: 2 };
    // else
    return { a: 1, b: undefined };
}

вы должны использовать аннотацию типов:

function foo(): { a: number; b?: number } {
    // if Something
    return { a: 1, b: 2 };
    // else
    return { a: 1 };
}

Колбэки в стиле Node

Функции колбеков в стиле Node (вроде (err,somethingElse)=>{ /* что-нибудь */ }) обычно вызываются с err равным null, если нет ошибок. Как бы то ни было, в общем случае просто используйте проверку на истинность:

fs.readFile('someFile', 'utf8', (err, data) => {
    if (err) {
        // сделать что-нибудь
    } else {
        // ошибок нет
    }
});

Когда создаёте свои собственные API, в подобных ситуациях нормально использовать null для согласованности. Но, честно говоря, лучше посмотреть в сторону промисов, тогда вам вообще не придется париться из-за пустых значений ошибок (вы обработаете их через .then и .catch).

Не используйте undefined для обозначения валидности

Для примера, ужасная функция:

function toInt(str: string) {
    return str ? parseInt(str) : undefined;
}

может быть написана намного лучше:

function toInt(
    str: string
): { valid: boolean; int?: number } {
    const int = parseInt(str);
    if (isNaN(int)) {
        return { valid: false };
    } else {
        return { valid: true, int };
    }
}

JSON и сериализация

В стандарте JSON есть поддержка кодирования null, но нет undefined. При кодировании в JSON объекта, с атрибутом равным null, атрибут будет включён с нулевым значением, в то время как атрибут со значением undefined будет полностью исключён.

JSON.stringify({ willStay: null, willBeGone: undefined }); // {"willStay":null}

В результате, базы данных, основанные на JSON могут поддерживать значения null, но не undefined. Поскольку атрибуты со значением null закодированы, вы можете намеренно очистить атрибут, установив его значение в null перед кодированием и передав объект в удаленное хранилище.

Установка значений undefined может сэкономить память и затраты на передачу, поскольку названия атрибутов не будут закодированы. Однако, это может усложнить семантику очистки значений по сравнению с отсутствующими значениями.

Заключительная мысль

Команда TypeScript не использует null : TypeScript coding guidelines и это не вызвало никаких проблем. Дуглас Крокфорд считает, что null - это плохая идея и всем нам лучше использовать undefined.

Как бы то ни было, стиль NodeJS использует null для аргументов Error в качестве стандарта, посколько это означает Что-то в настоящий момент недоступно. Лично я не хочу разрываться между ними двумя, ведь большинство проектов используют библиотеки с разными подходами, и в обоих случаях использую просто == null.