The title might be a bit off, but I don't know how to describe the problem accurately.
I have the following code:
global.d.ts
type BuiltInListener<T extends Element, Key extends keyof HTMLElementEventMap> = (event: HTMLElementEventMap[Key], target: T) => any;
type Listener<T extends Element, TDetails = any> = (details: TDetails, target: T, event: EventUnion<TDetails>) => any;
type EventUnion<TDetails> = CustomEvent<TDetails>|HTMLElementEventMap[keyof HTMLElementEventMap];
type FilteredEventMap<TEvents> = keyof Omit<HTMLElementEventMap, keyof TEvents|'options'>;
type EventListenerConfig<T extends Element = Element, TEvents extends object = {}> = {
options?: Options;
[key: string]: Listener<T>|Options|undefined;
} & {
[Key in FilteredEventMap<TEvents>]?: BuiltInListener<T, Key>;
} & {
[Key in keyof Omit<TEvents, 'options'>]?: Listener<T, TEvents[Key]>;
};
interface EventTarget
{
on<T extends Element = Element, TEvents extends object = {}>(
selector: string|EventListenerConfig<T, TEvents>,
settings?: EventListenerConfig<T, TEvents>
): EventTarget;
trigger(event: string): EventTarget;
emit<TDetail = any>(event: string, detail?: TDetail, composed?: boolean): CustomEvent<TDetail>;
await<TDetail = any>(event: string): Promise<TDetail>;
}
Examples
// 1
this.shadow.on({
options: {
passive: false,
},
wheel: (e) => { // <--- inferred as any
},
});
// 2
const kaas: EventListenerConfig = {
options: {
passive: false,
},
wheel: (e) => { // <--- inferred as WheelEvent
},
};
// 3
this.shadow.on<Element>({
options: {
passive: false,
},
wheel: (e) => { // <--- inferred as WheelEvent
},
});
// 4
this.shadow.on<Button, { click: { action: string } }>('footer > [action]', {
click: ({ action }) => {}, // <--- inferred as { action: string }
wheel: (e) => {}, // <--- inferred as WheelEvent
});
why is it that 1 one gets inferred to any but all the other examples do work?
3 is a copy of 1 where I explicitly set the generic type, but it is the same as the default for that generic type.
4 is just an example of why I even have this ridiculous set up
To me it almost seems as if the compiler does something funky with default generic parameters, and that's why the EventListenerConfig of 1 goes through the [key: string] route instead of the [Key in FilteredEventMap<TEvents>] route.