typescript - Adding definitions for strongly typed events -


i'm trying figure out how efficiently add typed events project, keep running odd issues. ideally i'd able this:

declare class eventemitter<t> {     on<k extends keyof t>(event: k, fn: (...args: t[k]) => void, context?: any): void;     once<k extends keyof t>(event: k, fn: (...args: t[k]) => void, context?: any): void;     emit<k extends keyof t>(event: k, ...args: t[k]): boolean; }  interface myevents {     'eventa': [string, number];     'eventb': [string, { prop: string, prop2: number }, (arg: string) => void]; }  class myemitter extends eventemitter<myevents> {     // ... }  const myemitter = new myemitter();  myemitter.on('eventa', (str, num) => {}); myemitter.once('eventb', (str, obj, fn) => {});  myemitter.emit('eventa', 'foo', 3); 

the first issue apparently tuples aren't valid types rest parameters, despite being arrays of typed elements under hood (i believe being worked on). suppose that's fine if forgo typing emit method, , make events map function types instead of tuples. give benefit of little information arguments are.

declare class eventemitter<t> {     on<k extends keyof t>(event: k, fn: t[k], context?: any): void;     once<k extends keyof t>(event: k, fn: t[k], context?: any): void; }  interface myevents {     'eventa': (str: string, num: number) => void;     'eventb': (         str: string,         data: { prop: string, prop2: number },          fn: (arg: string) => void     ) => void; }  class myemitter extends eventemitter<myevents> {     // ... }  const myemitter = new myemitter();  myemitter.on('eventa', (str, num) => {}); myemitter.once('eventb', (str, obj, fn) => {}); 

at point i'm stumped. intellisense can infer proper signatures on or once, actual arguments types inferred event arguments on callback, makes no sense me. opened an issue few days ago, have yet response. i'm unsure if bug, or if i'm overlooking something.

in meantime, there efficient ways of doing this? i've thought adding overloads emitter class (here eventemitter using node typings):

class myemitter extends eventemitter {     on(event: 'eventa', fn: (str: string, num: number) => void);     on(event: 'eventb', fn: (         str: string,         data: { prop: string, prop2: number },         fn: (arg: string) => void     ) => void); } 

however requires me have actual implementation of on in class, , if want types once or emit have duplicate of event definitions. there better solution?

i commented on reported issue; seem buggy. work around annotating types on function parameters. it's annoying works:

myemitter.on('eventa', (str: string, num: number) => {}); // no errors myemitter.on('eventa', (str: string) => {}); // no error, [see faq](https://github.com/microsoft/typescript/wiki/faq#why-are-functions-with-fewer-parameters-assignable-to-functions-that-take-more-parameters) myemitter.on('eventa', (str: number) => {}); // error expected myemitter.on('eventa', (str: string, num: number, boo: boolean) => {}); // error expected 

you're right can't use tuple types rest parameters. can kind of work around converting tuple array, forgets order:

type asarray<t extends any[]> = (t[number])[]  declare class eventemitter<t extends {[k in keyof t]: any[]}> {   on<k extends keyof t>(event: k, fn: (...args: asarray<t[k]>) => void, context?: any): void;   once<k extends keyof t>(event: k, fn: (...args: asarray<t[k]>) => void, context?: any): void;   emit<k extends keyof t>(event: k, ...args: asarray<t[k]>): boolean; }  myemitter.emit('eventa', 2, 1); // oops, rest args string|number 

you can closer figuring out reasonable maximum number of function parameters (say 4) , declaring them way:

type tuplefunctionparams<t extends any[], r=void> = {     (a0: t[0], a1: t[1], a2: t[2], a3: t[3], a4: t[4], ...a5plus: asarray<t>): r   }  declare class eventemitter<t extends {[k in keyof t]: any[]}> {   on<k extends keyof t>(event: k, fn: tuplefunctionparams<t[k]>, context?: any): void;   once<k extends keyof t>(event: k, fn: tuplefunctionparams<t[k]>, context?: any): void;   emit<k extends keyof t>(event: k, a0?: t[k][0], a1?: t[k][1], a2?: t[k][2], a3?: t[k][3], a4?: t[k][4], ...args: asarray<t[k]>): boolean; } 

the arguments specify in tuple appear in right order. however, can still leave out arguments (see faq) , can still specify extra arguments unions of types in tuple:

myemitter.emit('eventa', 1, 2); // error expected myemitter.emit('eventa', 'one', 2); // works myemitter.emit('eventa', 'one'); // works, args optional myemitter.emit('eventa', 'one', 2, 3) // works, because subsequent args union myemitter.on('eventa', (str, num) => { }); // works myemitter.on('eventa', (str) => { }); // above myemitter.on('eventa', (str, num, rando) => { }); // above, rando string | number 

the best can after append bunch of never tuples:

interface myevents {   'eventa': [string, number, never, never, never, never, never];   'eventb': [string, { prop: string, prop2: number }, (arg: string) => void, never, never, never, never]; } 

now @ least parameters tend typed usefully:

myemitter.emit('eventa', 'one', 2, 3) // error on 3, should undefined myemitter.on('eventa', (str, num, rando) => { }); // rando never 

see on playground

okay, that's best can do. hope helps. luck!


Comments

Popular posts from this blog

php - Vagrant up error - Uncaught Reflection Exception: Class DOMDocument does not exist -

vue.js - Create hooks for automated testing -

Add new key value to json node in java -