Creating a parameter type in TypeScript by referencing another parameter type

Attempting to articulate our situation the best I can, given the lack of a specific title.

We have developed a package that facilitates communication between our various services and cloud functions. This package includes functions to call each service (e.g., an auth function for the authentication service). With an increasing number of endpoints for each service, it has become challenging to maintain due to the complexity of defining parameter types for these requests.

My goal is to create another type that outlines the parameter types for each endpoint of every service. For example, if we have an endpoint named getEmail which requires an id parameter, the type params would be structured like this:

type Params = {
  getEmail: {
    id: number;
  }
}

A simplified snippet of the code:

type FunctionName = 'foo' | 'bar' | 'baz';

type FunctionParams = {
  foo: {
    myVar: number;
  };
  bar: {
    myVar: string;
  };
  baz: Record<string, number>;
};

declare const sendRequest: (...args: any[]) => any;

const callFunction = (
  fn: FunctionName,
  params: FunctionParams[typeof fn],
  //                     ^^^^^^^^^ What should I put here?
) => {
  sendRequest(fn, params);
};

callFunction('foo', {
  myVar: 'this should fail',
  // This is allowed, as the type of the second parameter is:
  // { myVar: number; } | { myVar: string; } | Record<string, number>) => void
  // I want it to be { myVar: number; }
});

Answer №1

If you're looking to utilize generics, one approach is to parameterize the callFunction function in order to bind the fn argument to a specific string value. This can then be used to index into the FunctionParams type:

function callFunction<T extends FunctionName>(
  fn: T,
  params: FunctionParams[T],
) {
  sendRequest(fn, params);
};

callFunction('bar', {
  myVar: 'this will cause an error', //CRASH!
});

Furthermore, it's important not to spend unnecessary effort maintaining types. The definition of FunctionName can be simplified to:

type FunctionName = keyof FunctionParams

This way, whenever new parameters are added, the FunctionName type automatically reflects those changes. Take a look at this coding playground for a practical demonstration.

Answer №2

If necessary, you might consider a different type of approach, such as the one demonstrated below:


/**
 * Defining API Functions.
 */
interface ApiFunctions {
  foo: {
    myVar: number;
  };
  bar: {
    myVar: string;
  };
  baz: Record<string, number>;
}

/**
 * Generic API Functions method.
 *
 * @param args
 * @returns
 */
declare function sendRequest(...args: any[]): any;

/**
 * Utilizing the {@link sendRequest} wrapper.
 *
 * @param fn
 * @param params
 */
function callFunction<T extends keyof ApiFunctions>(
  fn: T,
  params: ApiFunctions[typeof fn]
) {
  sendRequest(fn, params);
}

callFunction('foo', {
  myVar: 'this should FAIL', // <<<--- FAIL!
});

callFunction('bar', {
  myVar: 'this should WORK',
});

For further assistance, you may find this article helpful:

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

Retrieve the output of forkJoin subscription in Angular 6 using rxJs 6

A Straightforward Example: const tasks = []; for (let i = 0; i < this.initialData.length; i++) { tasks.push( this.taskService.getDetails(this.id1[i], this.id2[i]) }; combineLatest(...tasks).subscribe(taskGroup => { console.log(task ...

PNG file is not displayed on React TSX page despite absence of any errors

I've implemented this initial design template from GitHub (https://github.com/vikpe/react-webpack-typescript-starter). Additionally, I'm utilizing react-bootstrap and have a container that includes a backgroundImage. const heroImage = require(&q ...

Adding 30 Days to a Date in Typescript

Discovering Typescript for the first time, I'm attempting to calculate a date that is (X) months or days from now and format it as newDate below... When trying to add one month: const dateObj = new Date(); const month = dateObj.getUTCMonth() + 2; con ...

Exploring ways to retrieve a function-scoped variable from within an Angular subscribe function

Here's the scenario: I have a simple question regarding an Angular component. Inside this component, there is a function structured like this: somethingCollection: TypeSomething[] ... public deleteSomething(something: TypeSomething): void { // so ...

Can an "interface" be created to accept class instances without knowing their type or implementation in advance?

As a beginner in the world of dart, I recall how in typescript I was able to structure my code like this: // business-logic.ts interface Item { name: string; } interface Repository { addItem: (item: Item) => Promise<void>; } export class Busi ...

Constructing objects in TypeScript is a breeze with its C#-ins

It seems like finding a simple solution for my task is proving to be quite challenging. I have a class that accepts 10 parameters, most of which are optional. To simplify things, I will illustrate my dilemma using just 3 parameters. I wish to be able to i ...

When using a Redux action type with an optional payload property, TypeScript may raise complaints within the reducer

In my react-ts project, I define the following redux action type: type DataItem = { id: string country: string population: number } type DataAction = { type: string, payload?: DataItem } I included an optional payload property because there are tim ...

What is the process of destructuring an array containing objects?

Examining this JSON structure: { "Person": { "UID": 78, "Name": "Brampage", "Surname": "Foo" }, "Notes": [ { "UID": 78, "DateTime": "2017-03-15T15:43:04.4072317", "Person": { ...

How can ngx-modals detect when the modals have been closed?

I have integrated ngx-modals into my project and I am looking to add a boolean value to it. When the modals open, I want to set this boolean to "true", and when they close, I need it to be set to "false". Regardless of whether the modal is closed using the ...

Upon completion of a promise in an express middleware and breaking out of a loop, a 404 error is returned

In my efforts to retrieve an array of object (car) from express using database functions in conjunction with the stolenCarDb object, everything seems to be working fine. However, when attempting the following code snippet, it results in a 404 error w ...

Leveraging IntersectionObserver to identify the video in view on the screen

Our Objective I aim to implement a swipe functionality for videos where the URL changes dynamically based on the ID of the currently displayed video. Challenges Faced Although I managed to achieve this with code, there is an issue where the screen flashe ...

Issue with e2e.js file format in Cypress Support

I am trying to save Cypress screenshots into a report using a support file as recommended in the documentation. However, I keep encountering an error: Your supportFile is missing or invalid: support/e2e.js The supportFile must be a .js, .ts, .coffee file ...

Struggling to extract the hours and minutes from a date in IONIC: encountering an error stating that getHours is not a recognized

I encountered an issue while trying to extract the hours and minutes from a date in Ionic. Below is the code snippet from my .html file : <ion-datetime displayFormat="HH:mm" [(ngModel)]='timeEntered1' picker-format="h:mm"></ion-date ...

Problem with loading image from local path in Angular 7

I'm having trouble loading images from a local path in my project. The images are not rendering, but they do load from the internet. Can someone please help me figure out how to load images from a local path? I have already created a folder for the im ...

When I apply filtering and grouping to the table, the rows in the mat table disappear

When using mat-table, grouping works fine without filtering. However, once the table is filtered or if the search bar is focused, ungrouping causes the rows in the table to disappear. I am looking for a solution that allows me to group and ungroup the tabl ...

Acquiring the appropriate type from a type object using generics in TypeScript

I am working with an enum export const trackingKeys = { Form: 'form', Video: 'video', } as const I also have a type that assigns a type property to each key export type TrackingPropertiesByKey = { [trackingKeys.Form]: { bar : num ...

Some elements that fit the criteria of 'number | function' are not callable at all

Consider a basic function like this: export const sum = (num?: number) => { const adder = (n: number) => { if (!n) { return num; } num = (num && num + n) || n; return adder; }; return a ...

The issue encountered is when the data from the Angular form in the login.component.html file fails to be

I am struggling with a basic login form in my Angular project. Whenever I try to submit the form data to login.components.ts, it appears empty. Here is my login.component.html: <mat-spinner *ngIf="isLoading"></mat-spinner> & ...

The convention for TypeScript getter and setter methods

What is the recommended practice in TypeScript for defining class attributes? In the Angular 2 demo (The Heroes Tour from angular.io), all attributes are declared as public: export class Hero { id: number; name: string; } This allows for instantiat ...

Creating a component in Angular that utilizes multiple nested FormGroups

When attempting to nest multiple FormGroups, everything works smoothly if the template is not extracted into separate components. For instance, the following example functions as expected: Template <form [formGroup]="baseForm"> <div formGr ...