How do you type-safely check an LI click and read data-id?
Tests TypeScript narrowing with DOM event delegation. A strong answer uses closest to walk up from event.target, checks result with instanceof HTMLLIElement, then reads dataset.id. Red flag: casting event.target directly without handling nested child elements.
WHAT THIS TESTS: This tests TypeScript narrowing with DOM APIs and understanding of event delegation mechanics. Interviewers want to see if you know that event.target points to the actual deepest element clicked, which could be a child inside the LI such as a span or icon. They also want to see how you narrow the generic EventTarget type to HTMLLIElement without resorting to unsafe casts, and whether you understand the difference between structural typing and explicit type guards in the DOM.
A GOOD ANSWER COVERS: First, acknowledge that event.target is the actual clicked node, not necessarily the LI itself, so a direct instanceof HTMLLIElement check on event.target fails when users click nested children. Second, use a narrowing strategy such as calling closest on the target to walk up to the nearest LI, which is safer than chaining parentElement because closest crosses shadow boundaries if needed and stops at the first match. Third, verify that the returned element actually belongs to the delegated container to avoid matching LIs in nested sublists or outside the intended UL. Fourth, narrow the type using instanceof HTMLLIElement or a user-defined type guard so TypeScript understands the value is an HTMLLIElement and not just EventTarget or Element. Fifth, access the data-id attribute in a typed way via li.dataset.id which returns string or undefined, or alternatively li.getAttribute. Mention that event.target in the DOM typings is EventTarget, so you cannot access Element properties until you narrow.
COMMON WRONG ANSWERS: Answering with event.target.tagName equals LI without explaining that tagName alone does not narrow TypeScript types unless paired with a type assertion or predicate. Using event.target as HTMLLIElement unconditionally, which is an unsafe cast and does not handle nested elements. Checking instanceof on event.target directly without accounting for child elements, which breaks when clicks hit children inside the LI. Confusing event.target with event.currentTarget, where currentTarget is the UL that owns the listener. Failing to verify that the found LI belongs to the delegated list, which can cause bugs with nested lists.
LIKELY FOLLOW-UPS: How would you handle clicks on nested interactive elements like buttons or links inside the LI without triggering the delegate action? What if the LI is dynamically added after the listener is attached? How do you type the delegate listener itself so currentTarget is known to be HTMLUListElement? Would you extract a reusable type guard for LI checks across multiple handlers?
ONE CONCRETE EXAMPLE: ul.addEventListener with a click handler. Inside, cast event.target to HTMLElement and call closest with the selector li. If the result is null or if li.parentElement is not the ul, return early. Otherwise, use const id equals li.dataset.id. TypeScript narrows li to HTMLLIElement after the null check, so dataset is available. If you need to guarantee the id exists, add a second check like if id equals undefined return. This pattern is robust against nested markup and avoids unsafe type assertions.
Read the original → typescriptlang.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.