Перейти к основному содержимому

События и обработка событий в TypeScript

Разработчику

Дальше: TypeScript и React · Ветвления · События в JavaScript


В браузере UI строится на событиях (click, input, submit). TypeScript задаёт конкретный тип события — доступны правильные поля (value, clientX, preventDefault). В React используются синтетические обёртки — 21.md.

Маршрут: синтаксиссобытияTypeScript и React.

Подробная модель DOM и bubbling — события в JavaScript. Таблицы глобалов — Справочник — DOM и окружение.


Окружение и глобальные типы

TypeScript знает браузерные API через lib в tsconfig:

{
"compilerOptions": {
"lib": ["ES2022", "DOM", "DOM.Iterable"]
}
}
ОкружениеТипичный lib / пакет
БраузерDOM, DOM.Iterable
Node.js@types/node (без DOM)
Тесты (jsdom)DOM + настройки test runner

Глобалы вроде document, window, fetch появляются из lib, а не из вашего кода. Для своих глобалов (версия сборки, фичефлаги) — declare const в global.d.ts9.md §декларации.

Node-окружение (process, Buffer) типизируется через @types/node22.md.


Базовый обработчик

const button = document.querySelector("button");
if (!(button instanceof HTMLButtonElement)) {
throw new Error("Button not found");
}

button.addEventListener("click", (e: MouseEvent) => {
console.log(e.clientX, e.clientY);
});

Разбор:

  • querySelector возвращает Element | null — сужайте через instanceof12.md.
  • Для click на кнопке — MouseEvent, не общий Event.

Типы DOM-событий

СобытиеТип
click на элементеMouseEvent
input / change на <input>Event или InputEvent
input на <input type="text">лучше через target
submit на <form>SubmitEvent
keydownKeyboardEvent
const input = document.querySelector("#email");
if (!(input instanceof HTMLInputElement)) throw new Error("no input");

input.addEventListener("input", () => {
console.log(input.value);
});

const form = document.querySelector("form");
if (!(form instanceof HTMLFormElement)) throw new Error("no form");

form.addEventListener("submit", (e: SubmitEvent) => {
e.preventDefault();
const data = new FormData(form);
console.log(data.get("email"));
});

Разбор:

  • В input listener input.value уже типизирован после instanceof.
  • Альтернатива: (e.target as HTMLInputElement).value — слабее.

currentTarget vs target

list.addEventListener("click", (e: MouseEvent) => {
const target = e.target;
if (!(target instanceof HTMLElement)) return;

const item = target.closest("[data-id]");
if (!item) return;

const id = item.getAttribute("data-id");
console.log("clicked", id);
});

Разбор:

  • Делегирование: один listener на списке, клик по дочерним элементам.
  • closest ищет предка с атрибутом.

Клавиатура

window.addEventListener("keydown", (e: KeyboardEvent) => {
if (e.ctrlKey && e.key === "s") {
e.preventDefault();
console.log("Save shortcut");
}
});

Пользовательские события

type OrderCreatedDetail = { orderId: string };

const event = new CustomEvent<OrderCreatedDetail>("order:created", {
detail: { orderId: "ord-1" },
bubbles: true,
});

window.addEventListener("order:created", (e: Event) => {
if (!(e instanceof CustomEvent)) return;
const detail = e.detail as OrderCreatedDetail;
console.log(detail.orderId);
});

window.dispatchEvent(event);

Разбор:

  • Generic у CustomEvent<T> зависит от lib DOM; иногда detail типизируют вручную после проверки.
  • Typed emitter в приложении — 28.md.

React: кратко

const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};

События React не нативные MouseEvent — обёртки с похожим API. Не смешивайте addEventListener и React на одном узле без причины.


Частые ошибки

ОшибкаЧто делать
(e: Event) вездеконкретный тип
as HTMLInputElement без проверкиinstanceof
Забыли preventDefault на submitперезагрузка страницы
onclick в HTMLaddEventListener в TS-модуле

Практика

  1. Форма: input, submit, вывод ошибок валидации.
  2. Горячая клавиша Ctrl+S с KeyboardEvent.
  3. CustomEvent с detail и подписчик.
  4. Делегирование клика по списку data-id.
  5. Тот же сценарий в React — 21.md.

Смежные статьи