I am trying to extract the keys of an interface (using keyof
) that match an event handler pattern - that is (CustomEvent) => void
.
I have a method that seems to work and does extract only keys that match (CustomEvent) => void
, however, it also extracts keys who's type is () => void
.
Is there a way to extract only keys of types that conform to (CustomEvent) => void
?
The method is based on the questions Filter interface keys for a sub-list of keys based on value type and Typescript : Filter on keyof type parameter
In the example below
export interface JayCustomEvent {}
type EventHandler = (e: JayCustomEvent) => void;
type FilteredKeys<T, U> = { [P in keyof T]: P extends string ? (T[P] extends U ? P : never) : never}[keyof T];
interface AComponentEvent extends JayCustomEvent {}
interface AComponent {
anEvent(e: AComponentEvent): void,
noParamFunction(): void,
someOtherFunction(a: number): void
someProp: string
}
let a: FilteredKeys<AComponent, EventHandler> = 'anEvent'
let b: FilteredKeys<AComponent, EventHandler> = 'noParamFunction'
let c: FilteredKeys<AComponent, EventHandler> = 'someOtherFunction'
let d: FilteredKeys<AComponent, EventHandler> = 'someProp'
I expect assignment a
to be working, while b
, c
and d
should not.
However, assignments a
and b
are valid, while only c
and d
are not.
CodePudding user response:
Your mapping utility seems fine.
I think you might be surprised to learn that functions with lower arity are subtypes of compatible higher-arity ones. Consider the following example:
type AssignableTo<T, U> = T extends U ? true : false;
type Fn0Params = () => void;
type Fn1Param = (p: unknown) => void;
declare const ex1: AssignableTo<Fn0Params, Fn1Param>;
ex1 // true
type Fn0ParamsNever = (...args: never) => void;
declare const ex2: AssignableTo<Fn0ParamsNever, Fn1Param>;
ex2 // false
So, by typing your noParamFunction
as having parameters of type never
, you can prevent it from being assignable to EventHandler
:
noParamFunction(...args: never): void
Perpendicular to your question: If you're actually working with
CustomEvent
s, then you might want to use(event: CustomEvent<JayCustomEvent>) => void