Как в TypeScript проверить null и undefined: ловушки и лучшие решения

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему «просто if (value)» может вас подвести?

Каждый разработчик, переходящий с JavaScript на TypeScript, рано или поздно сталкивается с вопросом: «Is there a way to check for both null and undefined?». Этот вопрос набрал тысячи просмотров на Stack Overflow — и неспроста. На первый взгляд, задача кажется тривиальной: написать if (value != null) или if (value). Но дьявол, как всегда, кроется в деталях.

В этой статье мы разберём топ ошибок, которые допускают новички (и не только) при проверке на null и undefined в TypeScript. Вы узнаете, почему нестрогое сравнение может быть опаснее, чем кажется, как работает оператор Nullish Coalescing (??) и когда стоит использовать пользовательские защиты типов. Поехали!

Ошибка №1: Использование if (!value) для всех ложных значений

Самый распространённый антипаттерн — это попытка проверить наличие значения через отрицание:

// ❌ Плохо
function processInput(input: string | null | undefined) {
  if (!input) {
    console.log('Ввод отсутствует');
    return;
  }
  console.log(input.toUpperCase());
}

processInput(''); // ❌ Выведет "Ввод отсутствует", хотя строка пустая, но НЕ null и НЕ undefined

Проблема в том, что !value преобразует любое ложное значение в true. В TypeScript (и JavaScript) ложными считаются: false, 0, '' (пустая строка), NaN, null, undefined. Если ваша бизнес-логика допускает пустую строку или число 0, такой код сломается.

Как избежать: Используйте строгую проверку на null и undefined через === или !==.

Ошибка №2: Слепое доверие value == null

Многие разработчики знают, что value == null (двойное равно) проверяет и null, и undefined одновременно. Это действительно работает:

// ✅ Работает, но...
function check(value: string | null | undefined) {
  if (value == null) {
    console.log('value is null or undefined');
  }
}

Однако двойное равно в TypeScript — это источник неочевидных багов. Например, null == undefined — это true, но null == false — это false. А что насчёт '' == false? Тоже true. Нестрогое сравнение может вести себя непредсказуемо, особенно в сложных условиях.

Как избежать: В TypeScript value == null — это официально разрешённый паттерн (линтер часто его допускает), но для новичков лучше придерживаться явного подхода: value === null || value === undefined.

Ошибка №3: Игнорирование undefined при работе с объектами

Иногда разработчики проверяют только null, забывая про undefined:

// ❌ Опасно
interface User {
  name?: string;
  age: number | null;
}

function greet(user: User) {
  if (user.age !== null) {
    console.log(`Age: ${user.age}`);
  }
  // Если age не передан (undefined), код выполнится!
}

greet({ name: 'Alice' }); // Выведет "Age: undefined"

Поле age может быть null, number или undefined (если не указано в интерфейсе). Проверка только на null пропускает undefined.

Как избежать: Всегда проверяйте оба значения, если тип допускает оба. Или используйте оператор Optional Chaining (?.) и Nullish Coalescing (??).

Лучший способ: Строгая проверка с помощью ===

Самый надёжный и очевидный способ — явно сравнить с null и undefined:

// ✅ Отлично
function isNullOrUndefined(value: unknown): value is null | undefined {
  return value === null || value === undefined;
}

// Использование
function display(value: string | null | undefined) {
  if (isNullOrUndefined(value)) {
    console.log('Значение отсутствует');
  } else {
    console.log(value);
  }
}

Этот код работает предсказуемо и не зависит от ложных значений. Кроме того, мы использовали предикат типа (value is null | undefined), что позволяет TypeScript сузить тип в блоке if.

Профессиональный подход: Nullish Coalescing (??) и Optional Chaining

TypeScript (и современный JavaScript) предлагает два элегантных оператора для работы с null и undefined:

  • Optional Chaining (?.) — безопасный доступ к свойствам. Если значение null или undefined, цепочка прерывается и возвращается undefined.
  • Nullish Coalescing (??) — возвращает правую часть, только если левая — null или undefined (в отличие от ||, который реагирует на все ложные значения).
// Пример с Optional Chaining
interface Config {
  theme?: {
    color?: string;
  };
}

const config: Config = {};
const color = config.theme?.color ?? 'blue'; // 'blue', так как theme undefined
console.log(color); // blue

// Пример с Nullish Coalescing
const count = 0;
const displayCount = count ?? 10; // 0, потому что 0 — не null и не undefined
const displayCountFallback = count || 10; // 10, потому что 0 — ложное значение!
console.log(displayCount); // 0
console.log(displayCountFallback); // 10

Этот подход особенно полезен при работе с API, где поля могут отсутствовать.

Пользовательская защита типа (Type Guard)

Если вам нужно часто проверять на null и undefined, создайте свою функцию-утилиту:

// Кастомный type guard
function isNil(value: unknown): value is null | undefined {
  return value == null; // Здесь двойное равно уместно, но можно и ===
}

// Использование
function process(value: string | null | undefined) {
  if (isNil(value)) {
    // TypeScript знает, что value — null | undefined
    return;
  }
  // Здесь value — string
  console.log(value.length);
}

Такой подход делает код чище и переиспользуемым. Многие библиотеки (например, Lodash) имеют функцию isNil, но вы легко можете написать свою.

Блоги

Рекомендаций по книгам