Работа с DOM
Работа с DOM
Технический фундамент DOM-типизации
DOM-типизация в TypeScript строится вокруг двух рисков:
- элемент может отсутствовать (
null); - элемент может быть не того класса (
ElementvsHTMLInputElement).
Поэтому безопасный паттерн почти всегда такой:
- получить элемент;
- сузить тип через проверку (
if,instanceof); - только потом вызывать специфичные свойства/методы.
Эта дисциплина резко снижает рантайм-падения из-за изменений верстки.
Почему TypeScript особенно полезен в DOM-коде
DOM-код часто ломается из-за двух типичных причин: элемент не найден (null) или найден элемент не того типа. В JavaScript это обычно всплывает в рантайме. TypeScript заставляет тебя учесть эти сценарии заранее.
Ключевой момент: в TypeScript доступ к DOM строится через строгие типы элементов и проверки на null.
Проверь себя: почему вызов button.addEventListener(...) без проверки может упасть на проде?
Типы у querySelector
querySelector возвращает Element | null, потому что элемент может отсутствовать.
const button = document.querySelector('#save-btn');
if (button) {
button.addEventListener('click', () => {
console.log('save');
});
}
Смотри, что важно: проверка if (button) это narrowing, после нее TypeScript понимает, что button не null.
Уточнение типа элемента
Иногда нужен конкретный тип (HTMLInputElement, HTMLButtonElement и т.д.).
const input = document.querySelector('#email') as HTMLInputElement | null;
if (input) {
console.log(input.value);
}
Если ты уверен в ожидаемом типе элемента, можно подсказать его через generic-параметр (и не писать as).
const input = document.querySelector<HTMLInputElement>('#email');
if (input) {
console.log(input.value);
}
Смотри, что важно: generic не убирает необходимость проверки на null и не делает проверку типа в рантайме. Если нет 100% уверенности, безопаснее сузить через instanceof.
Лучше использовать безопасные проверки, а не слепое приведение, если не уверен в структуре DOM.
const el = document.querySelector('#email');
if (el instanceof HTMLInputElement) {
console.log(el.value);
}
События и типы
Обработчики событий имеют типизированный объект события.
const form = document.querySelector('#signup-form');
if (form) {
form.addEventListener('submit', (event) => {
event.preventDefault();
});
}
Если нужен доступ к target, часто требуется дополнительное сужение типа.
Смотри, что важно: event.target это элемент, по которому кликнули/ввели (часто вложенный), а event.currentTarget это элемент, на котором висит обработчик. В типах оба начинаются как EventTarget | null, поэтому обычно делают narrowing через instanceof.
const input = document.querySelector<HTMLInputElement>('#email');
if (input) {
input.addEventListener('input', (event) => {
const el = event.currentTarget;
if (el instanceof HTMLInputElement) {
console.log(el.value);
}
});
}
Проверь себя: почему event.target не всегда имеет свойства value без проверки?
Атрибуты, dataset и приведение
dataset возвращает строки или undefined, это важно учитывать.
const card = document.querySelector('[data-id]');
if (card instanceof HTMLElement) {
const id = card.dataset.id; // string | undefined
}
Здесь часто путаются: даже если атрибут обычно есть, TypeScript требует учитывать случай отсутствия.
Реальные микро-сценарии
- Форма логина.
Типы гарантируют, что ты работаешь именно с HTMLInputElement, а не абстрактным Element.
- Кнопка отправки.
Проверка null защищает от падения на страницах, где кнопка не отрендерилась.
- Список карточек.
Типизированный dataset помогает безопасно извлекать id перед API-запросом.
Частые ошибки новичков
- Использовать
!(non-null assertion) везде без проверки. - Делать слепое
as HTMLInputElementбез уверенности. - Игнорировать, что
querySelectorможет вернутьnull. - Считать, что
event.targetвсегда нужного типа.
Анти-провал: если элемент критичен, проверяй его существование и тип сразу после выбора.
Что будет, если изменить входные данные
Если селектор #email больше не существует в шаблоне, код с проверкой if (input) безопасно пропустит логику. Код без проверки упадет на чтении input.value. Именно такие "мелкие" изменения в верстке часто ломают JS-логику без типовой дисциплины.
Проверь себя: когда уместно использовать !, и почему это должно быть исключением, а не правилом?
Краткий итог
- DOM-API в TypeScript требует учитывать
nullи точные типы элементов. - Безопасный DOM-код строится через narrowing (
if,instanceof). - События и
targetчасто требуют дополнительного уточнения типов. - Слепые приведения и non-null assertions повышают риск рантайм-багов.
- TypeScript в DOM особенно полезен для устойчивости UI при изменениях верстки.