You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// Implementation of event emitter - sample without all methodstypeListener<Event>=(event: Event)=>void;// Here the "key: K" complains "An index signature parameter type must be either 'string' or 'number'"typeListeners<Events,Kextendsstring&keyofEvents>={[key: K]: Listener<Events[K]>[]}classEventEmitter<Eventsextends{[key: string]: unknown}>{
#listeners: Listeners<Events,keyofEvents>constructor(){this.#listeners ={}}}// UsagetypeMyEvents={connect: voidreconnect: numbererror: Errordisconnect: void}constemitter=newEventEmitter<MyEvents>()
🙁 Actual behavior
I am getting ts errors even though the types work as expected. See // usage section
🙂 Expected behavior
The keyof returns the same type as the constraint generic and thus will be valid as index signature.
So, does that mean it is currently not possible to do?
btw. why is K extends string & keyof Events not enough to make it an indexable type since it requires 'string' or 'number' and the generic K must be a string and also keyof Events?
Thanks guys,
the error message wasnt clear to me, but know I understand that you cant use a union of types which you get from the keyof - e.g. type union = 'a' | 'b' | 'c' to index but instead use it in either Record which handles the union case or use mapped type like {[key in K]: ... } instead of {[key: K]:
Lastly, do you know why there is the last error at the .push(listener) line in .on() method ts playground
Argument of type 'Listener<Events[Type]>' is not assignable to parameter of type 'Listener<Events[keyof Events]>'.
Type 'Events[keyof Events]' is not assignable to type 'Events[Type]'.
Type 'keyof Events' is not assignable to type 'Type'.
'keyof Events' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint 'string | number | symbol'.
Type 'string | number | symbol' is not assignable to type 'Type'.
'string | number | symbol' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint 'string | number | symbol'.
Type 'string' is not assignable to type 'Type'.
'string' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint 'string | number | symbol'.(2345)
it seems to me that those types should be the same, no? One is Events[keyof Events] (so basically any values of the Events) and the second one is the concrete event Events[Type] where the type is defined as constraint of keyof Events
on<TypeextendskeyofEvents>(type: Type,listener: Listener<Events[Type]>): Unsubscribe{if(!type)thrownewError('Cannot call <EventEmitter>.on without a type');if(!listener)thrownewError('Cannot call <EventEmitter>.on without a listener');this.#listeners[type]=this.#listeners[type]||[];this.#listeners[type].push(listener);return()=>{this.off(type,listener);}}
Activity
MartinJohns commentedon Jul 12, 2021
Having
keyof Tas an indexer type would require #26797.alesmenzel commentedon Jul 12, 2021
So, does that mean it is currently not possible to do?
btw. why is
K extends string & keyof Eventsnot enough to make it an indexable type since it requires 'string' or 'number' and the generic K must be a string and also keyof Events?MartinJohns commentedon Jul 12, 2021
The type
string & keyof Eventsis the same askeyof Events. And a union of keys can not be an index signature. You can use a mapped type instead.RyanCavanaugh commentedon Jul 12, 2021
This works without issue
alesmenzel commentedon Jul 12, 2021
Thanks guys,
the error message wasnt clear to me, but know I understand that you cant use a union of types which you get from the keyof - e.g. type union = 'a' | 'b' | 'c' to index but instead use it in either Record which handles the union case or use mapped type like
{[key in K]: ... }instead of{[key: K]:Lastly, do you know why there is the last error at the
.push(listener)line in.on()method ts playgroundit seems to me that those types should be the same, no? One is
Events[keyof Events](so basically any values of the Events) and the second one is the concrete eventEvents[Type]where the type is defined as constraint ofkeyof Eventsalesmenzel commentedon Jul 12, 2021
Looks like I had a mistake in the mapped type, working version for anyone interested
ts playground
Thanks Martin Johns (@MartinJohns) and Ryan Cavanaugh (@RyanCavanaugh)