Avatar ·

Как исправить ошибку TS2533: "Object is possibly 'null' or 'undefined'" в TypeScript?

Всем привет! Я начал изучать TypeScript и столкнулся с ошибкой, которую не могу понять. У меня есть простой код, где я получаю элемент из DOM и пытаюсь изменить его текст: ```typescript const button = document.querySelector('.my-button'); button.textContent = 'Нажми меня'; ``` TypeScript выдает ошибку: ``` error TS2533: Object is possibly 'null' or 'undefined'. ``` Я понимаю, что `querySelector` может вернуть `null`, если элемент не найден, но я точно знаю, что элемент существует на странице. Как мне объяснить TypeScript, что я уверен в наличии элемента? Пробовал добавлять проверки, но все равно путаюсь. Подскажите, пожалуйста, какие есть способы решить эту проблему? Мне важно, чтобы код оставался безопасным, но при этом не захламлялся лишними проверками. <<>>

Полное объяснение ошибки TS2533 и способы её решения

Ошибка TS2533 возникает, когда TypeScript обнаруживает, что значение может быть null или undefined, но вы пытаетесь обратиться к его свойству или методу без проверки. Это одна из ключевых фич TypeScript для обеспечения типобезопасности.

Рассмотрим ваш пример. document.querySelector возвращает тип Element | null. TypeScript не позволяет просто так обратиться к .textContent, потому что в runtime может оказаться, что элемента нет.

Вот основные способы решения:

1. Оператор утверждения ненулевого значения (Non-null assertion operator) — !

Самый простой, но и самый опасный способ. Вы говорите TypeScript: "Я знаю лучше, не проверяй".

const button = document.querySelector('.my-button')!;
button.textContent = 'Нажми меня'; // Ошибки нет

Минус: Если элемент всё-таки отсутствует, вы получите ошибку в runtime (Cannot read properties of null). Используйте только когда 100% уверены.

2. Проверка на null (Type guard)

Самый безопасный способ — явная проверка:

const button = document.querySelector('.my-button');
if (button !== null) {
    button.textContent = 'Нажми меня'; // TypeScript понимает, что здесь button — не null
}

Можно использовать сокращённую форму:

if (button) {
    button.textContent = 'Нажми меня';
}

3. Оператор опциональной цепочки (Optional chaining) — ?.

Позволяет безопасно обратиться к свойству, если значение null или undefined:

const button = document.querySelector('.my-button');
button?.textContent = 'Нажми меня'; // Если button null — ничего не произойдёт

Но для присваивания это работает не всегда корректно. Лучше использовать для чтения.

4. Оператор нулевого слияния (Nullish coalescing) — ??

Позволяет задать значение по умолчанию, если переменная null или undefined:

const button = document.querySelector('.my-button') ?? document.createElement('button');
button.textContent = 'Нажми меня';

Рекомендация

  • Для DOM-элементов, которые гарантированно существуют, используйте ! — это экономит время и делает код чище.
  • Для данных из API или пользовательского ввода всегда используйте проверки (if).
  • Настройте strictNullChecks: true в tsconfig.json — это стандарт для современных проектов.

Пример правильного подхода для вашего случая:

// Если элемент точно есть (например, в статической вёрстке)
const button = document.querySelector('.my-button')!;
button.textContent = 'Нажми меня';

// Если есть сомнения — проверяем
const maybeButton = document.querySelector('.my-button');
if (maybeButton instanceof HTMLButtonElement) {
    maybeButton.textContent = 'Нажми меня'; // Ещё и типизировали как кнопку
}
<<>>

Альтернативный подход: использование строгих проверок с типами

Можно пойти дальше и создать вспомогательные функции для безопасного получения элементов:

function getElementOrThrow<T extends Element>(selector: string): T {
    const element = document.querySelector<T>(selector);
    if (!element) {
        throw new Error(`Элемент "${selector}" не найден`);
    }
    return element;
}

// Использование
const button = getElementOrThrow<HTMLButtonElement>('.my-button');
button.textContent = 'Нажми меня'; // Без ошибки, с явным типом

Этот подход даёт несколько преимуществ:

  • Чёткое указание ожидаемого типа элемента
  • Понятная ошибка в runtime, если элемент отсутствует
  • Переиспользуемость кода

Если не хотите бросать исключения, можно возвращать null и обрабатывать его:

function safeQuerySelector<T extends Element>(selector: string): T | null {
    return document.querySelector<T>(selector);
}

const button = safeQuerySelector<HTMLButtonElement>('.my-button');
if (button) {
    button.textContent = 'Нажми меня';
}

Такой подход сочетает безопасность типов с читаемостью кода. Особенно полезен в больших проектах, где много работы с DOM.