TypeScript allows typing of custom events to ensure correct handling of their objects, especially in web applications or when working with the pub/sub pattern. To type events, the CustomEvent type with a parameterized generic type is commonly used:
interface MyEventDetail { username: string; age: number; } const event = new CustomEvent<MyEventDetail>('user:create', { detail: { username: 'alice', age: 30 } }); document.addEventListener('user:create', (e: CustomEvent<MyEventDetail>) => { console.log(e.detail.username); });
Key nuances:
CustomEvent<T>) matches both when creating the event and when handling it.any or object.Often asked: "Can the event object be directly typed as
CustomEvent<Type>in the handler if the event is created by a third-party library?"
The correct answer: Only if it is absolutely certain that the library generates events via CustomEvent with a generic. Otherwise, typing will be incorrect, and runtime errors may occur.
Example:
document.addEventListener('some-event', (e: Event) => { // Incorrect: 'e' is not necessarily CustomEvent const detail = (e as CustomEvent<{ id: number }>).detail; // This will cause an error if the event is not CustomEvent });
Story
On the project, we used a pub/sub abstraction and emitted events through a plain Event instead of CustomEvent. We expected to access event.detail, but the base Event type does not have such a field, leading to silent undefined and subtle bugs.
Story
Part of the team typed custom events as any to implement handlers for various scenarios "faster". As a result, errors arose later due to passing different data structures via detail, and TypeScript did not signal any mismatches.
Story
In one project, they began using event typing as CustomEvent<string>, while passing complex objects via detail. They mistakenly serialized detail, leading to the need for additional JSON.parse on the handler side and loss of typing within TypeScript.