Is there a way to make the following work without having to define an implementation in the subclass which simply calls the superclass or unnecessarily repeats the non-speci
For a less verbose solution, check out ee-ts. It provides a typed EventEmitter
class with support for:
strict event names (think string
unions for the type
in emit(type, ...args)
)
type-checked emit
and on
calls (never again emit the wrong type or expect the wrong types in a listener)
import { EventEmitter as EE } from 'ee-ts'
type User = { name: string }
// All possible events must be explicitly defined as methods here.
// The return type can be non-void because the `emit` method returns the last non-void value.
// The return type can never be required, because `void` is implicitly added to every event.
interface Events {
login(user: User): void
logout(): string
}
// Make your subclass generic to let users add their own events.
class App extends EE {
/* ... */
}
let app = new App()
// The type of `user` is inferred.
app.on('login', user => {
console.log(user.name) // user.name is string
})
// Invalid argument types are caught.
app.one('login', (invalid: boolean) => {}) // [ts] Type 'User' is not assignable to type 'boolean'.
// Invalid return values are caught.
app.one('logout', () => true) // [ts] Type 'boolean' is not assignable to type 'string | void'.
// Unknown event names are caught.
app.emit('invalid') // [ts] Argument of type '"invalid"' is not assignable to parameter of type '"login" | "logout"'.