Issue
Can the keys that map to a specified value type be extracted from a TypeScript interface treated as a map?
For example, consider the WindowEventMap
in lib.dom.d.ts
...
interface WindowEventMap
extends GlobalEventHandlersEventMap, WindowEventHandlersEventMap {
"abort": UIEvent;
"afterprint": Event;
"beforeprint": Event;
"beforeunload": BeforeUnloadEvent;
"blur": FocusEvent;
// ...
}
The keyof
operator can retrieve all keys:
type FullKeyList = keyof WindowEventMap;
// yields: FullKeyList = "'abort' | 'afterprint' | 'beforeprint' | ..and 93 more"
Is there an expression to filter only the keys mapping to the value type KeyboardEvent
?:
// Objective:
type SubKeyList = SomeExpression<WindowEventMap, KeyboardEvent>;
// would result in: SubKeyList = "'keyup' | 'keypress' | 'keydown'"
Attempted Solution
A Conditional Type expression exists in TypeScript:
T extends U ? X : Y
A possible solution could be:
type Filter<K extends keyof M, V, M> = M[K] extends V ? K : never;
It filters individual types:
// Passes this test:
type FilteredExample1 = Filter<'keyup', KeyboardEvent, WindowEventMap>;
// outputs FilteredExample1 = "'keyup'"
// Passes this test:
type FilteredExample2 = Filter<'blur', KeyboardEvent, WindowEventMap>;
// outputs FilteredExample2 = "never"
But can it filter a union of types and return a new union? It seems promising at first:
// Passes this test:
type FilteredUnionExample1 = Filter<'keyup' | 'keydown', KeyboardEvent , WindowEventMap>;
// outputs FilteredUnionExample1 = "'keyup' | 'keydown'"
However, the union fails if one or more members fail:
// Fails this test:
type FilteredUnionExample2 = Filter<'keyup' | 'keydown' | 'blur', KeyboardEvent, WindowEventMap>;
// outputs FilteredUnionExample2 = "never" (not sub-union "'keyup' | 'keydown'")
// So, it also fails the desired usage:
type AllKeysUnion = keyof WindowEventMap;
type FilteredUnionExample3 = Filter<AllKeysUnion, KeyboardEvent , WindowEventMap>;
// outputs FilteredUnionExample3 = "never" (not sub-union "'keyup' | 'keypress' | 'keydown'")
Is there a solution to this challenge?