Leveraging TypeScript generics for indexing

Trying to establish type information for event listeners by using generics on the .on() function.

type Name = "error" | "connected";

type Callback = {
    error: (err: Error) => void,
    connected: (err: number) => void,
};

function on<T extends Name>(eventName: T, callback: Callback[T]): void { }

on("error", (err) => err.stack);
on("connected", (err) => err.stack);

Expected an error for the connected event due to the attempt to use a number as an Error, but received no type hinting for the callback functions.

If all function definitions in Callback match, functionality starts to work. For example:

type Callback = {
    error: (err: Error) => void,
    connected: (err: Error) => void,
};

View the GIF demonstrating the issue in VS Code:

https://i.sstatic.net/nIxu1.gif

Is there an error in my approach?

Answer №1

It appears that the behavior of the inference engine in the compiler is quite peculiar.

My theory on this: When you specify the second parameter as Callback[T], the engine will attempt to deduce the value of T based on the argument type. Therefore, if you do not explicitly define the type for the arrow function, the inference engine will assume the parameter for the arrow function to be any and then make an educated guess at T based on the arrow function type. (Enabling strict mode will result in an error stating that the parameter implicitly has type any).

There are two potential solutions to this issue:

1. Adopt a two-function approach, where T is established in the initial call and remains known for the subsequent call where the parameter is supplied:

type Callback = {
    error: (e: Error) => void
    connected: (e: string) => void
};
function on<T extends Name>(eventName: T) { 
    return function(callback: Callback[T])
    {
    };
}

on("error")(e=> e.stack);
on("connected")(e=> e.substr(1));

2. If the only variation between functions is the parameter type and you are utilizing version 2.8 or older (not yet released at the time of writing but available in RC, which can be obtained via npm install -g typescript@rc), you can focus solely on the differing argument, thereby preventing the inference engine from utilizing the second parameter to deduce T:

type Arg0<T> =  T extends (p1: infer U) => any ? U: never;
function on<T extends keyof Callback>(eventName: T, callback: (e: Arg0<Callback[T]>) => void) : void { }

on("error", e=> e.stack);
on("connected", e=> e.substr(1));

Answer №2

Looks like you might be overcomplicating things.

Events could potentially be organized in an enum.

enum EVENTS{
  ERROR = "ERROR",
  CONNECTED = "CONNECTED"
}

Instead of a generic method, it might be beneficial to have a specific method.

function on(eventName: EVENTS, callback: (v: any) => void): void { }

Here are a couple of examples:

on(EVENTS.ERROR, (v: any) => {
  if(v instanceof Error){
    console.log(v.stack);
  }
});

on(EVENTS.CONNECTED, (v: any) => {
  console.log(v);
});

It can be challenging to determine the type of the event value.

Apologies. Here is an updated function.

function on<T>(eventName: EVENTS, callback: (v: T) => void): void { }

on<Error>(EVENTS.ERROR, (err) => {  
    console.log(err.stack);  
});

on<any>(EVENTS.CONNECTED, (v) => {
  console.log(v);
});

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

Mastering the proper implementation of observables, async/await, and subscribing in Angular

I have a JSON file located at assets/constants/props.json. Inside this file, there is a key called someValue with the value of abc. The structure of the JSON file can be seen in the following image: https://i.stack.imgur.com/MBOP4.jpg I also have a serv ...

Creating circular artwork with PixiJS: A step-by-step guide

I am trying to create a circular image with specific height and width dimensions, but have not found a satisfactory solution. Currently, I can achieve this using a texture, however it is drawn multiple times in the same position. const test = new Graphic ...

How to refresh a page manually in Angular 2

How can I have a page in Angular reload only once when a user visits it? This is my attempt: In the homepage component, I added the following code: export class HomepageComponent implements OnInit { constructor() { } ngOnInit() { location.relo ...

Discover how TypeScript's strictNullChecks feature can help you identify null values with ease in your functions

Since Javascript often requires me to check if a value is `!= null && != ''`, I decided to create a function that checks for empty values: const isEmpty = (variable: any, allowEmptyString?: boolean): boolean => { return variable == null | ...

Having trouble importing a TypeScript module from the global node_modules directory

I have a library folder located in the global node modules directory with a file named index.ts inside the library/src folder //inside index.ts export * from './components/button.component'; Now I am trying to import this into my angular-cli ap ...

Creating a customized HTTP class for Bootstrap in Angular 2 RC 5

During my experience with Angular 2 RC 4, I encountered a situation where I needed to create a class called HttpLoading that extended the original Http class of Angular2. I managed to integrate this successfully into my project using the following bootstr ...

What is the best way to assign JSON values to my class property?

I've been working on a weather application that showcases the current weather of 5 different cities. By clicking on each city, users can access a detailed view displaying the 5-day forecast for that particular location. Currently, I have defined a we ...

typescript push in react native is a crucial step to enhance performance and optimize

I've been diving into TypeScript within the realm of React Native. Oddly, when I translated a certain snippet to vanilla JavaScript, the application worked flawlessly. However, upon converting it back to TypeScript, an error message popped up stating ...

Next.js page freezes when Axios request is made causing the tab to become unresponsive

Curious about how to troubleshoot (or where to start) with my current Axios problem. I am working on a Next.js project (12.3) and have an axios interceptor hook that manages all of my internal requests. The interceptor functions properly on every action/pa ...

Struggling to convert my VueJS component from JavaScript to TypeScript, feeling a bit lost

I am new to VueJS and I am facing a challenge converting my VueJS project to use TypeScript. I have been trying to bind functions to certain variables in JavaScript, but I am struggling with accomplishing the same in TypeScript. Even though there are no er ...

Tips for customizing the appearance of a label when a MUI Radio Button is selected

Hello everyone, I am attempting to customize the label text color of a radio button to turn blue when selected. https://i.stack.imgur.com/btSc2.jpg HERE IS THE CODE FOR MY MUI BUTTON SO FAR import * as React from "react"; import Radio from &quo ...

ANGULAR: Issue with filtering an array by clicking a button is not functioning

I've been attempting to apply a filter to my array by using modulo on the id when clicking multiple buttons. I initially tried using pipe but was advised to stick with .filter(). Despite watching numerous online tutorials, I keep encountering errors o ...

Tips for efficiently awaiting outcomes from numerous asynchronous procedures enclosed within a for loop?

I am currently working on a search algorithm that goes through 3 different databases and displays the results. The basic structure of the code is as follows: for(type in ["player", "team", "event"]){ this.searchService.getSearchResult(type).toPromise ...

Exploring the concept of inheritance and nested views within AngularJS

I've encountered a challenge while setting up nested views in AngularJS. Utilizing the ui-router library has been beneficial, but I'm facing issues with separate controllers for each view without proper inheritance between them. This results in h ...

Is there a way to reset static data in a TypeScript subclass? (or alternative method for managing global data)

I have a particular set of static data that I would like to access through an API using basic logic. Specifically, this data pertains to metadata about Java classes. My approach involved incorporating the API into a few static methods within a class, alon ...

I'm looking to send a response with data using Nest JS API and Postman. How can I accomplish this

As I work on setting up my server using Nest Js, I encountered an issue while trying to fetch data from Postman to test the API urls. Unfortunately, I keep receiving empty responses from the server or undefined values from the postman request. Below is a s ...

What could be causing my items to appear twice and as blank elements?

I'm feeling a bit lost here as to why my code isn't functioning correctly. For some reason, it's not displaying the predefined items that I've set up. Any guidance or assistance would be greatly appreciated. Dealing with Angular errors ...

Limit the types of function parameters to only one option within a union type parameter

I have a collection of tuples that I can use to define variables: type KnownPair = ["dog", "paws"] | ["fish", "scales"]; const goodPair: KnownPair = ["dog", "paws"]; //@ts-expect-error you cannot mix them: const badPair: KnownPair = ["dog", "scales"]; I ...

Modify one specific variable within my comprehensive collection on Firebase Firestore

After clicking the button, I need to update a variable. The variable in question is "bagAmount" and it is stored in my firestore collection. Here is a link to view the Firestore Collection: Firestore Collection Currently, I am able to update one of the va ...

What are some methods for utilizing the "name" attribute within React components?

About My Coding Environment Utilizing TypeScript and ReactJS The Issue with Using name as an Attribute Encountering the following error: Type '{ name: string; "data-id": string; "data-type": string; }' is not assignable to ...