Utilizing TypeScript's dynamic class method parameters

I've been working on integrating EventBus with TypeScript and I'm trying to create a dynamic parameter event in the emit method. How can I achieve this?

interface IEventBusListener {
  (...params: any[]): void
}

class EventBus {
  constructor(private listeners: Record<string | symbol, IEventBusListener[]> = {}) { }

  on(event: string | symbol, callback: IEventBusListener) {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }

    this.listeners[event].push(callback);
  }

  off(event: string | symbol, callback: IEventBusListener) {
    if (!this.listeners[event]) {
      throw new Error(`Нет события: ${event.toString()}`);
    }

    this.listeners[event] = this.listeners[event].filter(
      listener => listener !== callback
    );
  }

  emit<T extends keyof typeof this.listeners>(event: T, ...args: any[]) {
    if (!this.listeners[event]) {
      throw new Event(`Нет события: ${event.toString()}`);
    }

    this.listeners[event].forEach(listener => {
      listener(...args);
    });
  }
}

I was hoping for autocomplete and type checking, but unfortunately it's not functioning as expected.

Answer №1

To effectively handle event callbacks, it is important to establish a clear map of relations between event names and their corresponding parameters. Take the following example:

class EventDispatcher<P extends Record<string, any[]>> {
    on<E extends keyof P>(eventName: E, callback: (...args: P[E]) => void) {
        // Implement logic here
    }
}

type EventTypeMap = {
    'click': [number]
    'hover': [string]
}
class EventHandler extends  EventDispatcher<EventTypeMap> {

}

const handler = new EventHandler();

Alternatively, consider using the eventemitter3 library for advanced functionality.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Excessive geolocation position responses in Angular 5

I am trying to implement an Angular 5 component that will continuously fetch my current location every 3 seconds if it has changed. Here is a snippet of my code: export class WorkComponent implements OnInit { constructor(private userService: UserService ...

Having trouble with importing a TypeScript class: encountering a "cannot resolve" error message

Could you lend me your expertise? I'm puzzled by this issue that seems to be quite simple and straightforward: export class Rectangle { height: number = 0 width: number = 0 constructor(height: number, width: number) { this. ...

How to selectively make properties optional in Typescript conditions

Currently, I am working on creating a utility type to unwrap nested monads of Options in my code. Here is the progress I have made so far: export interface Option<T> { type: symbol; isSome(): boolean; isNone(): boolean; match<U>(fn: Mat ...

Leverage the compiler API to perform type inference

Exploring TypeScript's compiler API for basic type inference has proven to be a challenge with limited helpful information found in documentation or online searches. My goal is to create a function inferType that can determine and return the inferred ...

What is the reason for Google Chrome extension popup HTML automatically adding background.js and content.js files?

While using webpack 5 to bundle my Google Chrome extension, I encountered an issue with the output popup HTML. It seems to include references to background.js and content.js even though I did not specify these references anywhere in the configuration file. ...

Injecting multiple instances of an abstract service in Angular can be achieved using the following techniques

I am fairly new to Angular and currently trying to make sense of the code written by a more experienced developer. Please excuse me if I'm not adhering to the standard communication practices and vocabulary. There is an abstract class called abstract ...

Utilizing a React hook to render and map elements in a function

Can the hook return function be assigned to a render map in React? In this example, we have the socialAuthMethodsMap map with the onClick parameter. I tried to assign the signInWithApple function from the useFirebaseAuth hook, but it violates React's ...

Vite HMR causes Vue component to exceed the maximum number of recursive updates

After making changes to a nested component in Vue and saving it, I noticed that the Vite HMR kept reloading the component, resulting in a warning from Vue: Maximum recursive updates exceeded... Check out the online demo on stackblitz. Make a change in Chi ...

Issues arise with Jest tests following the implementation of the 'csv-parse/sync' library

Currently utilizing NestJs with Nx.dev for a monorepo setup. However, I've come across an issue after installing csv-parse/sync - my Jest tests are now failing to work. Jest encountered an unexpected token Jest failed to parse a file due to non-stand ...

What is the best way to access JavaScript built-ins in Typings when faced with name conflicts?

I am currently in the process of updating the Paper.js Typings located on GitHub at this repository: github.com/clark-stevenson/paper.d.ts Within Paper.js, there exists a MouseEvent class, which is not an extension of JavaScript's MouseEvent, but ra ...

Angular: updating a single route triggers multiple 'NavigationEnd' events

I am currently working with Angular 4 and have written the following code in one of my components to detect when a route has changed and reload the page: ngOnInit() { this.router.events.subscribe((value) => { if (value instanceof Navi ...

Is there a different approach available since the array function "some" does not restrict type even when a type predicate is implemented?

It is expected that when using the array function "some" along with a type predicate and return statement, the TypeScript compiler would narrow down the type of dashArray. Is it reasonable to expect this behavior from the TypeScript compiler or am I incor ...

Tips for customizing Material UI CSS default properties in React

I'm currently working on a React project and utilizing the 'Table' component from Material UI. The default CSS properties of this table, along with its components like TableHead, TableCell, and TableRow, are proving difficult to override whi ...

Having trouble with the removeEventListener OnDestroy not functioning properly in Angular 6 using Javascript?

I have been experimenting with using the removeEventListener function in my Angular component. I came across a helpful discussion on this topic: Javascript removeEventListener not working ... ngOnInit() { document.addEventListener('v ...

Declarations of Typescript React for Props for Specific Element

I am trying to specify types for certain elements like <button> or <input>, but I am unable to differentiate between specific element types. Here is an example: interface Props{ component: React.ComponentProps<"button"> | nev ...

Receiving an error when triggering an onclick event for a checkbox in TypeScript

I am creating checkboxes within a table using TypeScript with the following code: generateTable(): void { var table = document.getElementById("table1") as HTMLTableElement; if (table.childElementCount == 2) { for (var x = 0; x < 2; x++) ...

Previous states in TypeScript

Just starting out with typescript and trying to work with user files in order to update the state. Currently facing a typescript error that I can't seem to figure out - Error message: Argument of type '(prev: never[]) => any[]' is not as ...

Troubleshoot: Issue with binding data from DynamicComponentLoader in Angular 2 template

My implementation involves the utilization of DynamicComponentLoader and is based on the Angular2 API Guide. https://angular.io/docs/ts/latest/api/core/DynamicComponentLoader-class.html The code structure I have set up looks like this: import {Page} fro ...

What is the method for utilizing a function's input type specified as "typeof A" to output the type "A"?

Check out this example from my sandbox: class A { doSomething() {} } class B {} const collection = { a: new A(), b: new B() } const findInstance = <T>(list: any, nonInstance: T): T => { for (const item in list) { if (lis ...

I'm unable to modify the text within my child component - what's the reason behind this limitation?

I created a Single File Component to display something, here is the code <template> <el-link type="primary" @click="test()" >{{this.contentShow}}</el-link> </template> <script lang="ts"> imp ...