There's no one way to do it, but there are alternative approaches. I personally prefer using tuple types in situations like this. Take a look:
type EventName = "abort" | "afterprint" | "animationend"; //html event names.
type EventListenerPropertyName = ["on" , EventName];
// example value
const x: EventListenerPropertyName = ['on', 'abort']
What you're essentially doing is combining two types similar to string concatenation. We can take this further by introducing generic types and constructing values. Check this out:
type EventName = "abort" | "afterprint" | "animationend"; //html event names.
type EventListenerPropertyName<T extends EventName> = ["on", T];
// value constructor
const makeEventListenerPropertyName = <T extends EventName>(a: T): EventListenerPropertyName<T> => ["on", a];
// parser to string
const eventListenerPropertyNameStr = <T extends EventListenerPropertyName<any>>(a: T): string => a[0] + a[1];
const x = makeEventListenerPropertyName('abort'); // x is ['on', 'abort']
const y = eventListenerPropertyNameStr(x); // y is onabort