(Answering my own question...)
So it turns out that tweaking EventHandlerMap as follows resolves the TypeScript error:
type EventHandlerMap<E extends Event> = {
// Previously: [Ev in E as Ev['type']]: (data: Ev['data']) => void
[T in E['type']]: (data: Extract<E, { type: T }>['data']) => void
}
I suppose TypeScript can rationalise this in a way that more closely matches my intent, since it ~iterates over the Event['type']s rather than the Events themselves. This will behave differently in the case where multiple members of the Event union have the same type attribute (but different data attributes).