What is the best way to create an array type in TypeScript that can link a function with its parameters?

In this scenario, I have encapsulated three functions within a literal block:

type AnyCallback = (...args: any) => unknown;
const callbackAlpha = (data: string) => {
  console.log(data);
  return data.length;
};

const callbackBeta = (data: string, prefix: string) => {
  console.log(data, prefix);
  return prefix + data;
};

const callbackGamma = (data: string, prefix: string, suffix: string) => {
  console.log(data, prefix, suffix);
  return prefix + data + suffix;
};

let allCallBack: Record<string, AnyCallback> = {
  callbackAlpha,
  callbackBeta,
  callbackGamma,
};

Next, the goal is to establish an array type that will record the function name and parameters when called. The structure of each member should look like this:

{
   name: //name of a function in the literal
   args: //parameters that needed when calling the function
}

Initially, this approach was attempted:

type TaskType = keyof typeof allCallBack;
type TaskQueue = {
  name: TaskType;
  args: Parameters<(typeof allCallBack)[TaskType]>;
}[];

This method turned out to be incorrect since it allows arbitrary values for "name" and accepts any parameters for the three functions.

Answer №1

When working with TypeScript, assigning a subtype (a more specific/narrow type) to a supertype (a less specific/broader type) does not preserve the subtype within the supertype.

For instance,

let a: "foo" = "foo";

let b: string = a;

// typeof b === string and not "foo"

This same behavior applies when assigning subtypes like "callbackAlpha" and (data: string) => number to their corresponding supertypes - string and (...args: any[]) => any in

Record<string, AnyCallback>

If you wish for TypeScript to maintain the exact type information, removing the type declaration (Record) for allCallback can provide a solution -

let allCallback = {
  callbackAlpha,
  callbackBeta,
  callbackGamma,
}

In this case, type inference works accurately and precise types are retained. To establish the TaskQueue type while maintaining the relationship between name and arguments, generics can be utilized -

type TaskType = keyof typeof allCallBack;
type TaskQueueItem<Name extends TaskType> = {
    name: Name,
    args: Parameters<(typeof allCallBack)[Name]>
}
type TaskQueue = TaskQueueItem<TaskType>[];

For example,

const myAlphaName = "callbackAlpha" as const;
const myBetaName = "callbackBeta" as const;
const myAlphaTask: TaskQueueItem<typeof myAlphaName> = {
    name: myAlphaName,
    args: ["a"]
};
const myBetaTask: TaskQueueItem<typeof myBetaName> = {
    name: myBetaName,
    args: ["a", "b"]
};
const myAllTasks: TaskQueue = [myAlphaTask, myBetaTask];

Explore TypeScript Playground

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

Exploring the use of generic types in TypeScript interfaces

I have the following two interfaces: export interface TestSchema<S> { data: S; description: string; } export type someType = 'option1' | 'option2'; export interface AnotherInterface { primary: string; secondary: someType; ...

Unable to access the 'filter' property of an undefined Array in Angular

Currently, I have an array named customerList: Array<String> = []; that is populated with values from a server-side function. Everything seems to be working well until I attempt to use the .filter() method on this array and encounter an error stating ...

Enhancing MongoDB subdocuments with JavaScript transformations

In my collection of applications (apps), each document contains a subgroup of users. I now face the task of updating a single user per application using a set of _ids specific to the apps collection, all within a JavaScript environment. A simple call to up ...

Transform FormData into a collection of objects

I am working on a form where the same set of fields repeats multiple times. After filling out the form, it is sent as FormData to the back-end. Currently, I have named the fields in the form like this: <input name="firstname" /> <input ...

Troubleshooting a JavaScript project involving arrays: Let it pour!

I'm a beginner here and currently enrolled in the Khan Academy programming course, focusing on Javascript. I've hit a roadblock with a project called "Make it rain," where the task is to create raindrops falling on a canvas and resetting back at ...

Utilizing enumerations to define properties within a class

In my current scenario, I have the following set of definitions: enum AccountTypes { CHECKING = 'Checking', SAVINGS = 'Savings', } export class BankAccount { id: number; label?: string; type: AccountTypes; constructor(ini ...

Tips for dynamically adding a new row to a table within a React application with TypeScript on click

I recently started learning React and TypeScript. I'm trying to figure out how to add a new row to a table when a user clicks a button (FontAwesomeIcon). I'm feeling a bit lost on where to begin with this. Here is the code snippet: import React ...

Obtain the URL string without any parameters in Angular 2

I am in the process of developing a web application using Angular2 (v. 2.1.1) for the frontend. Is there a way to extract the base URL, without any parameters, as a string? If my current URL is foo/bar/1/2/3, I am looking for a method to retrieve just fo ...

What's the reason behind using json_decode($array, TRUE)?

When sending a dictionary as JSON to a server, it's important to note that the dictionary contains only one key, which is an array of items. header('Content-type: application/json'); $request = json_decode(file_get_contents('php://inp ...

What is the best way to overlay 2D arrays in Java?

My current task involves navigating through a 2D array filled with blocked numbers, moving from the leftmost column to the rightmost column. The path I must create consists of "pieces" that connect to each other with only one block and do not overlap. Thes ...

"Upon compilation, the Angular app displays a blank screen instead of the expected

Recently, I attempted to create a client for a web application using Angular. I initiated the process with ng new client, and when I ran it at that point, it displayed the default webpage. Afterwards, I made modifications to the app.component.{html, css ...

Iterating through an array and displaying information according to the quantity in node.js

Hey everyone, I have a simple task at hand where I am working with the following array: {items:[{upc:a,quantity:2},{upc:b,quantity:3}]} My goal is to transform it into the format shown below: {products:[{barcode:a},{barcode:a},{barcode:b},{barcode:b},{bar ...

What is the best way to access a value from a service scope in Angular 2?

I am working on an Angular 4 app and have a function to print content. Below is a snippet from my TypeScript file: print(id) { // console.log(temp) this.templateservice.getTemplateById(id).subscribe(template => { if (!template.success) { this.sna ...

Tips for deleting a dynamic property from a predefined TypeScript base class

Let's say I have a third-party base class structured like this: export class Base { [k: string]: any; foo(): number; bar(): number; }; I need to inherit from this base class, but I want to remove the dynamic key in my object. Is there a ...

The horizontal combination of elements between a single-dimensional array and a two-dimensional array

It seems like I might be overlooking something simple. My goal is to generate a Cartesian product of arr1 (a 1d numpy array) and the ROWS of arr2 (a 2d numpy array). For example, if arr1 has 4 elements and arr2 has a shape of (5,2), the resulting output sh ...

Tips for transferring information between different components through a collaborative service

Attempting to send data using a subject to another component for the purpose of earning, but experiencing difficulty retrieving the data. Below is the provided code snippet: app.component.ts import { Component } from '@angular/core'; import { s ...

What is the procedure for inputting the settings for the export module in webpack?

I am currently working on setting up this webpack configuration file. However, I encountered an issue where the error message states that "any" is being used as a value instead of a type. How can I resolve this issue? module.exports:any = { ...

Outputting information from a multi-level array

I am facing a challenge in looping through an array retrieved from a database and displaying the Field_Name and Field_Value on a webpage. Array ( [0] => stdClass Object ( [Field_Name] => First Name [Field_Value] = ...

Managing API call limits using RxJs

Currently, I am in the process of developing an Angular application that utilizes an API provided by a social network. Unfortunately, this API has a restriction of only allowing 5 API calls per second. The typical solution to this limitation involves impl ...

What is the TypeScript's alternative to ReasonML's option type?

When using ReasonML, the option type is a variant that can be either Some('a) or None. If I were to represent the same concept in TypeScript, how would I go about it? ...