Типизация Event Handlers
Типизация обработчиков событий в TypeScript — указание правильного типа объекта события (
MouseEvent,KeyboardEvent,InputEvent) в параметре callback, чтобы TypeScript знал доступные свойства события и цели.
Зачем нужно
event.target в обработчиках событий имеет тип EventTarget | null, а не конкретный DOM-элемент. Без правильной типизации нельзя обратиться к input.value или button.disabled без кастов. TypeScript предоставляет точные типы для каждого вида события.
Где используется
- Обработчики DOM-событий: click, input, submit, keydown
- React event handlers:
onChange,onClick,onSubmit - Custom events через
CustomEvent<T> - WebSocket, EventSource события
- Drag-and-drop события
Основной контент
Встроенные типы событий
// Основные типы событий DOM
const btn = document.querySelector("button")!;
btn.addEventListener("click", (e: MouseEvent) => {
console.log(e.clientX, e.clientY); // координаты клика
console.log(e.target); // EventTarget | null
console.log(e.currentTarget); // EventTarget | null
});
document.addEventListener("keydown", (e: KeyboardEvent) => {
console.log(e.key, e.code); // "Enter", "Enter"
console.log(e.ctrlKey); // boolean
});
const input = document.querySelector("input")!;
input.addEventListener("input", (e: InputEvent) => {
const target = e.target as HTMLInputElement;
console.log(target.value); // string
});
Типизация target элемента
// e.target — EventTarget | null — нет value, checked и т.д.
// Нужен cast к конкретному типу элемента
function handleInput(e: Event): void {
const target = e.target as HTMLInputElement;
console.log(target.value); // OK после cast
}
// Лучше: использовать HTMLElementEventMap
function handleChange(e: Event): void {
if (e.target instanceof HTMLInputElement) {
console.log(e.target.value); // string — без cast, через narrowing
}
if (e.target instanceof HTMLSelectElement) {
console.log(e.target.value); // string
console.log(e.target.selectedIndex); // number
}
}
React event handlers
import { MouseEvent, ChangeEvent, FormEvent, KeyboardEvent } from "react";
// Клик по кнопке
function handleClick(e: MouseEvent<HTMLButtonElement>): void {
e.preventDefault();
console.log(e.currentTarget.disabled); // HTMLButtonElement
}
// Изменение input
function handleChange(e: ChangeEvent<HTMLInputElement>): void {
console.log(e.target.value); // string
console.log(e.target.checked); // boolean (для checkbox)
}
// Изменение select
function handleSelect(e: ChangeEvent<HTMLSelectElement>): void {
console.log(e.target.value); // string
console.log(e.target.selectedIndex); // number
}
// Submit формы
function handleSubmit(e: FormEvent<HTMLFormElement>): void {
e.preventDefault();
const form = e.currentTarget;
// form: HTMLFormElement
}
// Keyboard event
function handleKeyDown(e: KeyboardEvent<HTMLInputElement>): void {
if (e.key === "Enter") {
console.log(e.currentTarget.value);
}
}
Custom Events
// Создание custom event с типизированными данными
interface OrderPlacedEventDetail {
orderId: string;
total: number;
items: string;
}
// Отправка
const event = new CustomEvent<OrderPlacedEventDetail>("orderPlaced", {
detail: { orderId: "o-1", total: 99.99, items: ["item-1"] },
bubbles: true,
});
document.dispatchEvent(event);
// Получение
document.addEventListener("orderPlaced", (e: Event) => {
const custom = e as CustomEvent<OrderPlacedEventDetail>;
console.log(custom.detail.orderId); // string
console.log(custom.detail.total); // number
});
EventEmitter-паттерн
type EventMap = {
click: MouseEvent;
input: InputEvent;
submit: SubmitEvent;
};
type Handler<K extends keyof EventMap> = (event: EventMap[K]) => void;
function addHandler<K extends keyof EventMap>(
element: HTMLElement,
type: K,
handler: Handler<K>
): void {
element.addEventListener(type, handler as EventListener);
}
Частые ошибки
- Использовать
Eventвместо конкретного типа —Eventне имеетclientX,keyи т.д.; используйтеMouseEvent,KeyboardEvent. - Не проверять
e.targetперед доступом —e.targetэтоEventTarget | null; без instanceof-проверки нельзя обратиться кvalue. - React: путать
Event(DOM) и React SyntheticEvent — React оборачивает события; импортируйте типы изreact, не изlib.dom.d.ts. - Не вызывать
e.preventDefault()— для форм и ссылок без этого произойдёт стандартное действие браузера.
Связанные темы
- Типизация функций -- параметры и возврат
- Типы для callback функций
- Типизация React-компонентов
- _MOC TypeScript