JavaScript: Null vs Undefined

В JavaScript (Typescript) есть два низкоуровевых типа: null и undefined. И они предназначены для обозначения разных вещей:

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

Проверка

На самом деле придется иметь дело с обоими типами. Интересно то, что в JavaScript, при использовании == эти типы равны только друг с другом:

console.log(null == null); // конечно true
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.

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

Проверка на корневом уровне

На верхнем, корневом, глобальном уровне, если мы используем foo и foo в данный момент не существует при попытке использовать == null получим исключение ReferenceError. Это следует запомнить и использовать для проверки на глобальном уровне typeof

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

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

Вместо подобных

function foo() {
    return {a:1, b:2}
    return {a:1, b:undefined}
}

использовать аннотацию типов

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

Не использовать undefined как средство валидации

Пример "ужасной" функции

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

гораздо лучше сделать так

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

Стиль Node

Функции обратного вызова ((err, data) => {/* ...*/}) обычно вызываются с err установленным в null. И это не ошибка. Это стандартное правило, которое позволяет просто проверить есть ошибки или нет:

// node.js
fs.readFile('file', 'utf8', (err, data) => {
    if (err) {
        // has error
    } else {
        // no error
    }
})

Создавая код для Node.js следует придерживаться этого правила и использовать null.

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

Стандарт JSON поддерживает null, но не undefined.

null или undefined

Если посмотреть на правила команды Typescrypt, то увидим следующее - Use undefined. Do not use null. Однако в NodeJS код использует null для аргументов ошибок, как правило "Что-то недоступное сейчас". Наверное следует использовать null и undefined в зависимости от контекста, отдавая предпочтение undefined где это возможно.