HTMLElement/Event DOM Types
Last updated: 2026-10-22
HTMLElement/Event DOM Types
All DOM types come from TypeScript's built-in DOM lib: elements form an inheritance chain (EventTarget → Node → Element → HTMLElement → HTMLInputElement ...), and event objects carry target. Precise types come from narrowing: instanceof turns HTMLElement into a concrete subclass before you can touch .value; the same applies to e.target inside event handlers.
| Group | Engineering & Practice |
|---|---|
| Kind | Syntax |
| Strict mode | Not required |
ℹ️ Info The editor transpiles TypeScript to JavaScript and runs it (no type checking); see each example's Output for type-check results.
📝 Syntax
// Element narrowing: instanceof shrinks to a concrete subclass
function setText(el: HTMLElement, text: string) {
if (el instanceof HTMLInputElement) {
el.value = text; // .value exists only after narrowing
} else {
el.textContent = text; // plain elements use textContent
}
}
// Event narrowing: e.target is EventTarget | null
function onClick(e: Event) {
const t = e.target;
if (t instanceof HTMLInputElement) {
console.log(t.value); // available only after narrowing
}
}
⚙️ Parameters
| HTMLElement | The common base of all HTML elements: only generic members like .textContent/.style; querySelector returns Element by default, narrowed per tag via its generic parameter |
|---|---|
| HTMLInputElement | A concrete subclass for inputs with extras like .value/.name/.checked — the classic case of members available only after narrowing |
| Event.target | The element the event actually hit, typed EventTarget | null (it could be window, document, ...); almost always needs instanceof narrowing before use |
Returns:Narrowing has no return value — it only lets the compiler see the reference as a more precise type inside that branch, at zero runtime cost.
▶ Example
Edit the code below and click Try it to see the transpiled result:
Output:
input.value = "dev@example.com"
textContent = "Welcome"
input "email" = "dev@example.com"
clicked <HTMLElement>
(Runtime output after transpilation; stub classes stand in for the real DOM — the same instanceof code works against real elements in the browser, where DOM code actually runs)
💡 Notes
ℹ️ Info Worth memorizing: EventTarget → Node → Element → HTMLElement → concrete tags (HTMLInputElement/HTMLDivElement...). Higher in the chain means fewer members; the more specific the narrowing, the more you can use.
💡 Tip querySelector accepts a generic shortcut: document.querySelector<HTMLInputElement>('#email') returns HTMLInputElement | null, skipping instanceof; but nothing is validated at runtime — only safe if the element really is an input.
⚠️ Note Do not confuse e.target with e.currentTarget: target is the element actually hit (possibly not the one you bound the listener to, and possibly null), while currentTarget is the bound element. For form values prefer e.currentTarget (with precise callback signatures for specific event types).
🏷️ Related Engineering & Practice
💡 Related cases
More cases coming soon.
📚 Related tutorials
❓ FAQ
Q Why is document.querySelector's return type nullable?
A Because the selector may match nothing — strictNullChecks forces you to handle the null (an if guard or optional chaining). This is where strict mode is most famously annoying and most famously life-saving.
Q How do I read an input's value without a type error?
A Get an HTMLInputElement first: either querySelector<HTMLInputElement>("#id") or narrow an Element with instanceof HTMLInputElement. A plain HTMLElement has no .value — that member lives on the subclass.
Q How do I get a precise event type in addEventListener callbacks?
A The DOM lib overloads common event names: in el.addEventListener("input", (e) => ...) the e is automatically the matching Event subtype; combining with tag generics (querySelector<HTMLInputElement>) narrows target further. Only unoverloaded names need manual assertion.