Index signature is missing in type





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







0















I'm trying to make a typesafe EventEmitter, however I cannot enforce that the interface passed to the generic is of type EventMap without TypeScript complaining.



type EventHandler = () => void
type EventMap = Record<string, EventHandler>

interface EventListener {
handler: EventHandler
once: boolean
}

export class Emitter<Events extends EventMap> {
private listeners = new Map<keyof Events, EventListener>()

private addListener<E extends keyof EventMap>(type: E, listener: EventListener) {
const listeners = this.listeners.get(type) ||
this.listeners.set(type, [...listeners, listener])
}

@bind
public on<E extends keyof EventMap>(type: E, handler: Events[E]) {
this.addListener(type, { handler, once: false })
}
}


interface TestEvents {
test: (a: number) => void,
}

class Test extends Emitter<TestEvents> {}


Gives me



Type 'TestEvents' does not satisfy the constraint 'Record<string, EventHandler>'.
Index signature is missing in type 'TestEvents'.









share|improve this question























  • please provide definition of Record<,> type is there an indexer required? something like: [index:string] : string

    – Rafal
    Nov 26 '18 at 13:59











  • Record is a built in type in TypeScript. Edit: stackoverflow.com/questions/51936369/…

    – Sebastian Olsen
    Nov 26 '18 at 14:00




















0















I'm trying to make a typesafe EventEmitter, however I cannot enforce that the interface passed to the generic is of type EventMap without TypeScript complaining.



type EventHandler = () => void
type EventMap = Record<string, EventHandler>

interface EventListener {
handler: EventHandler
once: boolean
}

export class Emitter<Events extends EventMap> {
private listeners = new Map<keyof Events, EventListener>()

private addListener<E extends keyof EventMap>(type: E, listener: EventListener) {
const listeners = this.listeners.get(type) ||
this.listeners.set(type, [...listeners, listener])
}

@bind
public on<E extends keyof EventMap>(type: E, handler: Events[E]) {
this.addListener(type, { handler, once: false })
}
}


interface TestEvents {
test: (a: number) => void,
}

class Test extends Emitter<TestEvents> {}


Gives me



Type 'TestEvents' does not satisfy the constraint 'Record<string, EventHandler>'.
Index signature is missing in type 'TestEvents'.









share|improve this question























  • please provide definition of Record<,> type is there an indexer required? something like: [index:string] : string

    – Rafal
    Nov 26 '18 at 13:59











  • Record is a built in type in TypeScript. Edit: stackoverflow.com/questions/51936369/…

    – Sebastian Olsen
    Nov 26 '18 at 14:00
















0












0








0








I'm trying to make a typesafe EventEmitter, however I cannot enforce that the interface passed to the generic is of type EventMap without TypeScript complaining.



type EventHandler = () => void
type EventMap = Record<string, EventHandler>

interface EventListener {
handler: EventHandler
once: boolean
}

export class Emitter<Events extends EventMap> {
private listeners = new Map<keyof Events, EventListener>()

private addListener<E extends keyof EventMap>(type: E, listener: EventListener) {
const listeners = this.listeners.get(type) ||
this.listeners.set(type, [...listeners, listener])
}

@bind
public on<E extends keyof EventMap>(type: E, handler: Events[E]) {
this.addListener(type, { handler, once: false })
}
}


interface TestEvents {
test: (a: number) => void,
}

class Test extends Emitter<TestEvents> {}


Gives me



Type 'TestEvents' does not satisfy the constraint 'Record<string, EventHandler>'.
Index signature is missing in type 'TestEvents'.









share|improve this question














I'm trying to make a typesafe EventEmitter, however I cannot enforce that the interface passed to the generic is of type EventMap without TypeScript complaining.



type EventHandler = () => void
type EventMap = Record<string, EventHandler>

interface EventListener {
handler: EventHandler
once: boolean
}

export class Emitter<Events extends EventMap> {
private listeners = new Map<keyof Events, EventListener>()

private addListener<E extends keyof EventMap>(type: E, listener: EventListener) {
const listeners = this.listeners.get(type) ||
this.listeners.set(type, [...listeners, listener])
}

@bind
public on<E extends keyof EventMap>(type: E, handler: Events[E]) {
this.addListener(type, { handler, once: false })
}
}


interface TestEvents {
test: (a: number) => void,
}

class Test extends Emitter<TestEvents> {}


Gives me



Type 'TestEvents' does not satisfy the constraint 'Record<string, EventHandler>'.
Index signature is missing in type 'TestEvents'.






typescript






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 26 '18 at 13:54









Sebastian OlsenSebastian Olsen

2,61352353




2,61352353













  • please provide definition of Record<,> type is there an indexer required? something like: [index:string] : string

    – Rafal
    Nov 26 '18 at 13:59











  • Record is a built in type in TypeScript. Edit: stackoverflow.com/questions/51936369/…

    – Sebastian Olsen
    Nov 26 '18 at 14:00





















  • please provide definition of Record<,> type is there an indexer required? something like: [index:string] : string

    – Rafal
    Nov 26 '18 at 13:59











  • Record is a built in type in TypeScript. Edit: stackoverflow.com/questions/51936369/…

    – Sebastian Olsen
    Nov 26 '18 at 14:00



















please provide definition of Record<,> type is there an indexer required? something like: [index:string] : string

– Rafal
Nov 26 '18 at 13:59





please provide definition of Record<,> type is there an indexer required? something like: [index:string] : string

– Rafal
Nov 26 '18 at 13:59













Record is a built in type in TypeScript. Edit: stackoverflow.com/questions/51936369/…

– Sebastian Olsen
Nov 26 '18 at 14:00







Record is a built in type in TypeScript. Edit: stackoverflow.com/questions/51936369/…

– Sebastian Olsen
Nov 26 '18 at 14:00














2 Answers
2






active

oldest

votes


















2














You need to constrain it a bit differently, you want all keys of Events to be EventHandlers not necessarily for Events to have an index signature. You could use the following:



type EventHandler = (...a: any) => void

interface EventListener {
handler: EventHandler
once: boolean
}

export class Emitter<Events extends Record<keyof Events, EventHandler>> {
private listeners = new Map<keyof Events, EventListener>()

private addListener<E extends keyof Events>(type: E, listener: EventListener) {
const listeners = this.listeners.get(type) ||
this.listeners.set(type, [...listeners, listener])
}

public on<E extends keyof Events>(type: E, handler: Events[E]) {
this.addListener(type, { handler, once: false })
}
}


interface TestEvents {
test: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is number.


There is a problem with this approach however, typescript will not be able to infer the argument type if you have multiple events in the map (which you probably will)



interface TestEvents {
test: (a: number) => void,
test2: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is any


This is fixable, but the types get more complicated, see here for a very similar idea.






share|improve this answer
























  • So, I ended up making my emitter a bit different from the traditional node.js one, by using extends object and making my map's types represent a single value instead of having multiple arguments. Thank you for your answer though.

    – Sebastian Olsen
    Nov 26 '18 at 14:46



















1














Since Record is:



type Record<K extends string, T> = {
[P in K]: T;
}


you are lacking index as defined:



interface TestEvents {
test: () => void;
[index: string] : EventHandler;
}


also your test method is invalid as it does not meet EventHandler requirements that forces no parameters on that method.





How about this:



export interface EventMap extends Record<string, EventHandler>{
[index: string]: EventHandler;
event1: () => void;
event2: () => void;
}


now the required indexer exists but you force classes to have event1 and event2.






share|improve this answer


























  • Right, but that's not what I need. Adding an index signature there will also not make it typesafe, as there is no restriction on what events one can emit and listen to. There has to be a way to do this correctly.

    – Sebastian Olsen
    Nov 26 '18 at 14:10











  • This is what you asked for in your question. EventMap is just a record with index string and EventHandler as result so yes EventMap is enforced.

    – Rafal
    Nov 26 '18 at 14:13












Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53482637%2findex-signature-is-missing-in-type%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









2














You need to constrain it a bit differently, you want all keys of Events to be EventHandlers not necessarily for Events to have an index signature. You could use the following:



type EventHandler = (...a: any) => void

interface EventListener {
handler: EventHandler
once: boolean
}

export class Emitter<Events extends Record<keyof Events, EventHandler>> {
private listeners = new Map<keyof Events, EventListener>()

private addListener<E extends keyof Events>(type: E, listener: EventListener) {
const listeners = this.listeners.get(type) ||
this.listeners.set(type, [...listeners, listener])
}

public on<E extends keyof Events>(type: E, handler: Events[E]) {
this.addListener(type, { handler, once: false })
}
}


interface TestEvents {
test: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is number.


There is a problem with this approach however, typescript will not be able to infer the argument type if you have multiple events in the map (which you probably will)



interface TestEvents {
test: (a: number) => void,
test2: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is any


This is fixable, but the types get more complicated, see here for a very similar idea.






share|improve this answer
























  • So, I ended up making my emitter a bit different from the traditional node.js one, by using extends object and making my map's types represent a single value instead of having multiple arguments. Thank you for your answer though.

    – Sebastian Olsen
    Nov 26 '18 at 14:46
















2














You need to constrain it a bit differently, you want all keys of Events to be EventHandlers not necessarily for Events to have an index signature. You could use the following:



type EventHandler = (...a: any) => void

interface EventListener {
handler: EventHandler
once: boolean
}

export class Emitter<Events extends Record<keyof Events, EventHandler>> {
private listeners = new Map<keyof Events, EventListener>()

private addListener<E extends keyof Events>(type: E, listener: EventListener) {
const listeners = this.listeners.get(type) ||
this.listeners.set(type, [...listeners, listener])
}

public on<E extends keyof Events>(type: E, handler: Events[E]) {
this.addListener(type, { handler, once: false })
}
}


interface TestEvents {
test: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is number.


There is a problem with this approach however, typescript will not be able to infer the argument type if you have multiple events in the map (which you probably will)



interface TestEvents {
test: (a: number) => void,
test2: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is any


This is fixable, but the types get more complicated, see here for a very similar idea.






share|improve this answer
























  • So, I ended up making my emitter a bit different from the traditional node.js one, by using extends object and making my map's types represent a single value instead of having multiple arguments. Thank you for your answer though.

    – Sebastian Olsen
    Nov 26 '18 at 14:46














2












2








2







You need to constrain it a bit differently, you want all keys of Events to be EventHandlers not necessarily for Events to have an index signature. You could use the following:



type EventHandler = (...a: any) => void

interface EventListener {
handler: EventHandler
once: boolean
}

export class Emitter<Events extends Record<keyof Events, EventHandler>> {
private listeners = new Map<keyof Events, EventListener>()

private addListener<E extends keyof Events>(type: E, listener: EventListener) {
const listeners = this.listeners.get(type) ||
this.listeners.set(type, [...listeners, listener])
}

public on<E extends keyof Events>(type: E, handler: Events[E]) {
this.addListener(type, { handler, once: false })
}
}


interface TestEvents {
test: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is number.


There is a problem with this approach however, typescript will not be able to infer the argument type if you have multiple events in the map (which you probably will)



interface TestEvents {
test: (a: number) => void,
test2: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is any


This is fixable, but the types get more complicated, see here for a very similar idea.






share|improve this answer













You need to constrain it a bit differently, you want all keys of Events to be EventHandlers not necessarily for Events to have an index signature. You could use the following:



type EventHandler = (...a: any) => void

interface EventListener {
handler: EventHandler
once: boolean
}

export class Emitter<Events extends Record<keyof Events, EventHandler>> {
private listeners = new Map<keyof Events, EventListener>()

private addListener<E extends keyof Events>(type: E, listener: EventListener) {
const listeners = this.listeners.get(type) ||
this.listeners.set(type, [...listeners, listener])
}

public on<E extends keyof Events>(type: E, handler: Events[E]) {
this.addListener(type, { handler, once: false })
}
}


interface TestEvents {
test: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is number.


There is a problem with this approach however, typescript will not be able to infer the argument type if you have multiple events in the map (which you probably will)



interface TestEvents {
test: (a: number) => void,
test2: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is any


This is fixable, but the types get more complicated, see here for a very similar idea.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 26 '18 at 14:29









Titian Cernicova-DragomirTitian Cernicova-Dragomir

72.7k35270




72.7k35270













  • So, I ended up making my emitter a bit different from the traditional node.js one, by using extends object and making my map's types represent a single value instead of having multiple arguments. Thank you for your answer though.

    – Sebastian Olsen
    Nov 26 '18 at 14:46



















  • So, I ended up making my emitter a bit different from the traditional node.js one, by using extends object and making my map's types represent a single value instead of having multiple arguments. Thank you for your answer though.

    – Sebastian Olsen
    Nov 26 '18 at 14:46

















So, I ended up making my emitter a bit different from the traditional node.js one, by using extends object and making my map's types represent a single value instead of having multiple arguments. Thank you for your answer though.

– Sebastian Olsen
Nov 26 '18 at 14:46





So, I ended up making my emitter a bit different from the traditional node.js one, by using extends object and making my map's types represent a single value instead of having multiple arguments. Thank you for your answer though.

– Sebastian Olsen
Nov 26 '18 at 14:46













1














Since Record is:



type Record<K extends string, T> = {
[P in K]: T;
}


you are lacking index as defined:



interface TestEvents {
test: () => void;
[index: string] : EventHandler;
}


also your test method is invalid as it does not meet EventHandler requirements that forces no parameters on that method.





How about this:



export interface EventMap extends Record<string, EventHandler>{
[index: string]: EventHandler;
event1: () => void;
event2: () => void;
}


now the required indexer exists but you force classes to have event1 and event2.






share|improve this answer


























  • Right, but that's not what I need. Adding an index signature there will also not make it typesafe, as there is no restriction on what events one can emit and listen to. There has to be a way to do this correctly.

    – Sebastian Olsen
    Nov 26 '18 at 14:10











  • This is what you asked for in your question. EventMap is just a record with index string and EventHandler as result so yes EventMap is enforced.

    – Rafal
    Nov 26 '18 at 14:13
















1














Since Record is:



type Record<K extends string, T> = {
[P in K]: T;
}


you are lacking index as defined:



interface TestEvents {
test: () => void;
[index: string] : EventHandler;
}


also your test method is invalid as it does not meet EventHandler requirements that forces no parameters on that method.





How about this:



export interface EventMap extends Record<string, EventHandler>{
[index: string]: EventHandler;
event1: () => void;
event2: () => void;
}


now the required indexer exists but you force classes to have event1 and event2.






share|improve this answer


























  • Right, but that's not what I need. Adding an index signature there will also not make it typesafe, as there is no restriction on what events one can emit and listen to. There has to be a way to do this correctly.

    – Sebastian Olsen
    Nov 26 '18 at 14:10











  • This is what you asked for in your question. EventMap is just a record with index string and EventHandler as result so yes EventMap is enforced.

    – Rafal
    Nov 26 '18 at 14:13














1












1








1







Since Record is:



type Record<K extends string, T> = {
[P in K]: T;
}


you are lacking index as defined:



interface TestEvents {
test: () => void;
[index: string] : EventHandler;
}


also your test method is invalid as it does not meet EventHandler requirements that forces no parameters on that method.





How about this:



export interface EventMap extends Record<string, EventHandler>{
[index: string]: EventHandler;
event1: () => void;
event2: () => void;
}


now the required indexer exists but you force classes to have event1 and event2.






share|improve this answer















Since Record is:



type Record<K extends string, T> = {
[P in K]: T;
}


you are lacking index as defined:



interface TestEvents {
test: () => void;
[index: string] : EventHandler;
}


also your test method is invalid as it does not meet EventHandler requirements that forces no parameters on that method.





How about this:



export interface EventMap extends Record<string, EventHandler>{
[index: string]: EventHandler;
event1: () => void;
event2: () => void;
}


now the required indexer exists but you force classes to have event1 and event2.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 26 '18 at 14:19

























answered Nov 26 '18 at 14:07









RafalRafal

9,7772347




9,7772347













  • Right, but that's not what I need. Adding an index signature there will also not make it typesafe, as there is no restriction on what events one can emit and listen to. There has to be a way to do this correctly.

    – Sebastian Olsen
    Nov 26 '18 at 14:10











  • This is what you asked for in your question. EventMap is just a record with index string and EventHandler as result so yes EventMap is enforced.

    – Rafal
    Nov 26 '18 at 14:13



















  • Right, but that's not what I need. Adding an index signature there will also not make it typesafe, as there is no restriction on what events one can emit and listen to. There has to be a way to do this correctly.

    – Sebastian Olsen
    Nov 26 '18 at 14:10











  • This is what you asked for in your question. EventMap is just a record with index string and EventHandler as result so yes EventMap is enforced.

    – Rafal
    Nov 26 '18 at 14:13

















Right, but that's not what I need. Adding an index signature there will also not make it typesafe, as there is no restriction on what events one can emit and listen to. There has to be a way to do this correctly.

– Sebastian Olsen
Nov 26 '18 at 14:10





Right, but that's not what I need. Adding an index signature there will also not make it typesafe, as there is no restriction on what events one can emit and listen to. There has to be a way to do this correctly.

– Sebastian Olsen
Nov 26 '18 at 14:10













This is what you asked for in your question. EventMap is just a record with index string and EventHandler as result so yes EventMap is enforced.

– Rafal
Nov 26 '18 at 14:13





This is what you asked for in your question. EventMap is just a record with index string and EventHandler as result so yes EventMap is enforced.

– Rafal
Nov 26 '18 at 14:13


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53482637%2findex-signature-is-missing-in-type%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Wiesbaden

Marschland

Dieringhausen