Event Typing
Each event type gives you the right properties, like choosing the right CCTV feed at the checkpost.
Events are at the heart of interactive web applications. Every click, keypress, form submission, scroll, and touch is an event. And in TypeScript, every event has a SPECIFIC type that determines what properties you can access. Using the wrong event type is like watching the wrong CCTV feed at an RTA checkpost — you miss the crucial details you need.
Imagine the RTA control room. There are multiple CCTV feeds on the wall:
- The speed camera feed shows vehicle speeds (
MouseEvent— gives youclientX,clientY,button). - The license plate camera shows plate numbers (
KeyboardEvent— gives youkey,code,ctrlKey). - The traffic light camera shows signal status (
InputEvent— gives youdata,inputType). - The general overview camera shows the road (
Event— the base type, limited properties).
If you look at the speed camera feed to check a license plate, you won't see the plate clearly — wrong camera for the job. Similarly, if you type a keydown event as MouseEvent, you won't have access to .key or .code. TypeScript enforces that you use the right event type for the right event, ensuring you have access to the correct properties at compile time. Each event type is a specialized tool designed for a specific interaction — choose wisely and let TypeScript guide you to the right one.
TypeScript's DOM type definitions include a rich hierarchy of event types. Each one extends the base Event class and adds specific properties:
// Base type — minimal properties
Event
.type, .target, .currentTarget
.preventDefault(), .stopPropagation()
// UI events — adds view and detail
UIEvent extends Event
.view, .detail
// Mouse events — pointer interactions
MouseEvent extends UIEvent
.clientX, .clientY, .button
.altKey, .ctrlKey, .shiftKey
// Keyboard events — key presses
KeyboardEvent extends UIEvent
.key, .code, .repeat
.altKey, .ctrlKey, .shiftKey
// Focus events — element focus
FocusEvent extends UIEvent
.relatedTarget
// Input events — value changes
InputEvent extends UIEvent
.data, .inputType, .isComposing
The event type is determined by TWO things: the element the listener is attached to, and the event name string. TypeScript defines overloaded versions of addEventListener for each combination:
// TypeScript's addEventListener overloads:
element.addEventListener(
"click",
handler: (e: MouseEvent) => void
);
element.addEventListener(
"keydown",
handler: (e: KeyboardEvent) => void
);
element.addEventListener(
"input",
handler: (e: InputEvent) => void
);
// The element type ALSO matters:
// button.addEventListener("click")
// → MouseEvent
// But window.addEventListener("click")
// → MouseEvent (same, since click is
// always a mouse event)
TypeScript knows that when you attach a "click" event to a button, the handler receives a MouseEvent. When you attach a "keydown" event to an input, it's KeyboardEvent. This mapping is built into TypeScript's type definitions and works automatically. You rarely need to annotate the event parameter — TypeScript infers it from the element and event name.
There are several ways to handle events in TypeScript. Each approach has different ergonomics for typing the event parameter.
Method 1: addEventListener (most flexible)
const button =
document.querySelector("button");
if (button) {
button.addEventListener(
"click", (e) => {
// e is inferred as MouseEvent!
console.log(
e.clientX, e.clientY
);
}
);
}
const input =
document.querySelector("input");
if (input) {
input.addEventListener(
"keydown", (e) => {
// e is inferred as KeyboardEvent!
if (e.key === "Enter") {
console.log("Pressed Enter!");
}
}
);
}
Method 2: Inline Handlers (onclick, oninput, etc.)
// Inline handlers also type the event
button.onclick = (e) => {
// e is MouseEvent
console.log(e.clientX);
};
input.oninput = (e) => {
// e is InputEvent
console.log(e.data);
};
input.onkeydown = (e) => {
// e is KeyboardEvent
if (e.key === "Escape") {
input.blur();
}
};
Method 3: In Angular/React Templates
// Angular
// (click)="handleClick($event)"
// $event is MouseEvent
// React
// <button onClick={(e: React.MouseEvent) => {}}>
// React uses SyntheticEvent wrappers
Method 4: Custom Events
For custom events dispatched with new CustomEvent(), you can use the CustomEvent<T> generic type where T is the detail property type:
// Dispatch a custom event
const event = new CustomEvent<UserData>(
"user-login",
{ detail: { id: 1, name: "Imran" } }
);
window.dispatchEvent(event);
// Listen with typed detail
window.addEventListener(
"user-login",
(e: CustomEvent<UserData>) => {
// e.detail is UserData
console.log(e.detail.name);
}
);
TypeScript's event inference is comprehensive and covers virtually all standard DOM events. Let the inference work for you — annotate only when the context is ambiguous or when using custom events.
Event typing has a few common gotchas that can lead to frustrating compile errors or incorrect type assumptions.
Trap 1: Using the Base Event Type
When you annotate an event as Event instead of letting TypeScript infer the specific type, you lose access to type-specific properties:
button.addEventListener(
"click", (e: Event) => {
// e.clientX — ❌ not on Event
// e.button — ❌ not on Event
}
);
// FIX: Let TS infer or use specific type:
button.addEventListener(
"click", (e: MouseEvent) => {
console.log(e.clientX); // ✅
}
);
Trap 2: this in Event Handlers
When you use a class method as an event handler, this might not refer to the class instance. Use arrow functions or .bind(this) to preserve context:
class FormHandler {
button = document.querySelector("button")!;
handleClick(e: MouseEvent) {
// this might be the button, NOT the class!
console.log(this); // HTMLButtonElement!
}
constructor() {
// ❌ this is wrong in handleClick
this.button.addEventListener(
"click", this.handleClick
);
// ✅ Use arrow function
this.button.addEventListener(
"click", (e) => this.handleClick(e)
);
}
}
Trap 3: React Event Types are Different from DOM Types
React wraps native DOM events in SyntheticEvent objects. The property names are the same but the types are different. Always use React.MouseEvent, React.KeyboardEvent, etc. in React code:
// React — NOT DOM!
function handleClick(
e: React.MouseEvent<HTMLButtonElement>
) {
console.log(e.clientX);
}
// The generic parameter is
// the element type!
// <HTMLButtonElement>
// <HTMLInputElement>
// <HTMLDivElement>
Trap 4: target vs currentTarget
e.target is typed as EventTarget | null — it's the element that ORIGINATED the event. e.currentTarget is typed as the specific element type — it's the element the listener is attached to:
button.addEventListener(
"click", (e) => {
// e.currentTarget is HTMLButtonElement
// e.target is EventTarget | null
// e.target might be a child element!
// Always narrow e.target:
if (e.target instanceof HTMLElement) {
console.log(e.target.tagName);
}
}
);Here's your complete cheatsheet for event typing in TypeScript. Pin this to your mental board!
Common Event Types by Name:
click, dblclick, mousedown
→ MouseEvent
keydown, keyup, keypress
→ KeyboardEvent
input, change
→ InputEvent / Event
submit → SubmitEvent
focus, blur → FocusEvent
scroll, resize → Event
touchstart, touchend → TouchEvent
dragstart, drag, dragend → DragEvent
Adding Event Listeners:
// addEventListener (TS infers type)
element.addEventListener("click", (e) => {
// e is MouseEvent — inferred!
});
// Inline handler
element.onclick = (e: MouseEvent) => {
// e is MouseEvent
};
// Custom event
element.addEventListener(
"custom",
(e: CustomEvent<T>) => {
// e.detail is T
}
);
Key Rules:
- Let TypeScript infer the event type from addEventListener — don't over-annotate
- Use the most specific event type for full property access (MouseEvent vs Event)
- Use
KeyboardEvent.keyfor character values,.codefor physical keys - React uses
SyntheticEventsubtypes — import from React types - Custom events use
CustomEvent<T>where T is the detail type - Use
e.currentTargetfor the element with the listener, narrowe.target
The Golden Rule: "Event typing is like choosing the right CCTV feed at an RTA checkpost. MouseEvent is the speed camera, KeyboardEvent is the license plate camera, InputEvent is the traffic light camera. Pick the right feed, and you'll see exactly what you need, bhai!"
Key Takeaways
- Each DOM event has a specific type — MouseEvent, KeyboardEvent, InputEvent, etc.
- TypeScript infers the event type from the element and event name in addEventListener
- Use the specific event type (not the base Event) to access all relevant properties
- React uses SyntheticEvent subtypes — import from React types, not DOM types
- Custom events use CustomEvent
where T is the detail property type - e.currentTarget is the element with the listener; narrow e.target before using it
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login