Objective
I am currently developing a utility with the following interface:
interface F<M extends Record<string, Function>> {
(
listeners: M,
options: { filter: e => boolean }
): void
}
An important aspect here is the filter
option, which will receive events from all provided listeners.
My goal is to make the type of parameter e
dependent on the provided listeners:
f({
e1: (e: E1) => {}
}, {
filter: e => {} // <= `e` should be of type `E1`
})
f({
e1: (e: E1) => {},
e2: (e: E2) => {}
}, {
filter: e => {} // <= `e` should be of type `E1 | E2`
})
Solution Approach
I have discovered a way to achieve this:
interface F<T> {
<E extends keyof T>(
listeners: Pick<T, E>,
options: { filter: (e: Arg<T[E]>) => boolean }
): void
}
This solution effectively sets the correct parameter for the filter
function and enforces constraints on the listeners
, preventing the addition of keys not defined in T
.
Challenges Faced
The only minor issue I encountered is that TypeScript does not provide available key hints for the listeners
object.
For example, if I declare
let f: F<{ e1: Function, e2: Function }>
and use f({ ... })
with ctrl+space – it displays numerous unrelated suggestions instead of just e1
and e2
.
I understand that this behavior is expected with the current implementation. TypeScript cannot ascertain available options as I define the listeners
type, and TS can only validate it against the constraint of keyof T
.
Is there a possibility to enable typehints in this scenario without compromising the proper inference of the filter
callback parameter or the validation of keys for the listeners
parameter?
I experimented with various options in the sandbox, but none fully meet all my requirements.