DOM & Event Types

TypeScript's DOM types give every element and event a precise shape — from HTMLInputElement to MouseEvent — so querySelector , addEventListener , and event handlers are fully checked and autocompleted.

Learn DOM & Event Types in our free TypeScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

Part of the free TypeScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn

💡 Real-World Analogy

Think of the DOM as a building full of labelled rooms. querySelector is asking the receptionist for a room by name — they might hand you a key (the element) or shrug because it doesn't exist ( null ). Telling them "I want the input room" (the generic) means the key fits an input's locks specifically. Events are messages slipped under the door: a "click" note carries coordinates, a "keydown" note carries a key — same envelope ( Event ), different contents.

1. Element Types & querySelector Generics

Every DOM element has a precise type — HTMLInputElement , HTMLButtonElement , HTMLCanvasElement , all extending HTMLElement . querySelector takes a generic so you can say which one you expect, and it always returns YourType | null because the element may not be found.

2. Event vs MouseEvent vs KeyboardEvent

Events form a hierarchy : MouseEvent and KeyboardEvent extend the base Event , adding members like clientX or key . When you call addEventListener("click", ...) , TypeScript already knows the handler gets a MouseEvent — no casting needed.

3. Narrowing e.target & Typed Handlers

e.target is deliberately broad — EventTarget | null — because the event could have started on any descendant. To read element properties you narrow it with instanceof (or reach for the more specific e.currentTarget ). The handler-registration pattern itself is captured by a tiny typed event-emitter .

🎯 Your Turn

Narrow an event by its type field, just like you'd narrow a DOM event union. Fill in the two blanks marked ___ , then run it.

Common Errors (and the fix)

Pro Tips

📚 Keep Learning

Frequently Asked Questions

Mini-Challenge: A Typed Handler Map

No blanks this time — just a brief and a starting outline. Build the dispatcher yourself, run it, and check your output against the example in the comments.

🎉 Lesson Complete

Practice quiz

How do you tell TypeScript the exact element type from querySelector?

  • Cast with 'as any'
  • Use getElementById
  • Use the generic: document.querySelector<HTMLInputElement>("#email")
  • It is automatic for every selector

Answer: Use the generic: document.querySelector<HTMLInputElement>("#email"). The generic overload steers the result type, e.g. querySelector<HTMLInputElement>.

What is the result type of 'document.querySelector<HTMLInputElement>("#email")'?

  • HTMLInputElement | null
  • HTMLInputElement
  • Element
  • HTMLElement

Answer: HTMLInputElement | null. querySelector returns YourType | null because the element might not be found.

Why is querySelector's result possibly null?

  • A bug in the DOM lib
  • Because of strict mode only
  • It is never null
  • The selector might not match any element

Answer: The selector might not match any element. TypeScript types it as YourType | null to force you to handle the not-found case.

What is the return type of document.getElementById("box")?

  • HTMLDivElement
  • HTMLElement | null
  • Element
  • any

Answer: HTMLElement | null. getElementById has no generic and returns HTMLElement | null.

Which relationship is correct for event types?

  • MouseEvent and KeyboardEvent extend Event
  • Event extends MouseEvent
  • They are unrelated
  • KeyboardEvent extends MouseEvent

Answer: MouseEvent and KeyboardEvent extend Event. MouseEvent and KeyboardEvent both extend the base Event with extra members.

Inside addEventListener("click", handler), what type is the handler's event?

  • Event
  • KeyboardEvent
  • MouseEvent
  • any

Answer: MouseEvent. TypeScript infers the correct subtype (MouseEvent) from the known event name 'click'.

Which property does a KeyboardEvent have that the base Event does not?

  • clientX
  • key
  • preventDefault
  • target

Answer: key. KeyboardEvent adds members like 'key' and 'code'; clientX belongs to MouseEvent.

What is the type of e.target?

  • HTMLElement
  • Element
  • the specific element always
  • EventTarget | null

Answer: EventTarget | null. e.target is EventTarget | null, deliberately broad, so you must narrow it before use.

How do you safely read element-only members off e.target?

  • Cast with 'as HTMLInputElement'
  • Narrow with 'if (e.target instanceof HTMLInputElement)'
  • Ignore the type
  • Use e.target.value directly

Answer: Narrow with 'if (e.target instanceof HTMLInputElement)'. instanceof both checks and narrows, far safer than an 'as' cast.

Why does e.currentTarget often not need narrowing while e.target does?

  • currentTarget is always null
  • target is faster
  • currentTarget is the element the listener is attached to, so it can be typed directly
  • They are identical

Answer: currentTarget is the element the listener is attached to, so it can be typed directly. currentTarget is the element the listener is on and can be typed; target could be any descendant.

Continue this course

Related lessons