Determine the classification of the API namespace object's method type

There are two different API namespaces known as user and project. I am attempting to develop a versatile function that can determine the method type of the respective API namespace object.

const user = {
  async getUser(id: string): Promise<string> {
    return 'user'
  }
}
const project = {
  async getProject(): Promise<string> {
    return 'project'
  }
}
const apis = {
  user,
  project
};

interface UseApiQueryResponse<Data> {
  loading: boolean;
  data: Data | undefined;
}
export function useApiQuery<Params, Data>(api: ??, params: Params): UseApiQueryResponse<Data> {
  return api(params)
      .then((res: string) => ({loading: false, data: res}));
}

Example usage on the customer side:

useApiQuery(apis.user.getUser, {id: '1'});
useApiQuery(apis.project.getProject);

I am looking for a way to properly type the api argument, which could be any API method. It would be beneficial to automatically determine or restrict the return value type Data based on the type of api

TypeScript Playground

Answer №1

To begin, the first thing you must do is analyze the first argument of useApiQuery:

type AsyncFn = (...args: any[]) => Promise<any>

const useApiQuery = <Api extends AsyncFn, Data>(api: Api, ...params: Parameters<Api>) =>
  api(params)
    .then((res: string) => ({ loading: false, data: res }))
    

Considering that useApiQuery returns a promise, you only need to return UseApiQueryResponse<Data> because it will result in a

Promise<UseApiQueryResponse<Data>>
:

Furthermore, the line of code

useApiQuery(apis.user.getUser, { id: '1' })
is incorrect since getUser expects a string, not an object.

Below is the complete example:

const user = {
  async getUser(id: string) {
    return 'user'
  }
}
const project = {
  async getProject() {
    return 42
  }
}
const apis = {
  user,
  project
};

interface UseApiQueryResponse<Data> {
  loading: boolean;
  data: Data | undefined;
}

type AsyncFn = (...args: any[]) => Promise<any>

const useApiQuery = <Api extends AsyncFn>(api: Api, ...params: Parameters<Api>):
  Promise<UseApiQueryResponse<ReturnType<Api>>> =>
  api(params)
    .then((res) => ({ loading: false, data: res }))


const result1 = await useApiQuery(apis.user.getUser, '42'); // UseApiQueryResponse<Promise<string>>
const result2 = await useApiQuery(apis.project.getProject); // UseApiQueryResponse<Promise<number>>

export { }

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

A guide on crafting a precise description for the 'module.exports' feature

I have successfully exported a function in the following way: module.exports = function (options: any): RequestHandler { // Do something } Now, I am attempting to define the exported function properly. However, I am unsure if this is the correct appr ...

The dynamically included Angular2 component fails to render, while the statically added component displays correctly

Whenever a user clicks a button, I trigger the following method. @ViewChild("privs") privs: ElementRef; addPrivs() { this.privs.nativeElement .insertAdjacentHTML('beforeend', '<generic1>yey!</generic1>'); } This is h ...

I'd like to know the location of the SelectorEvent definition and the proper way to import it for type-checking

In my current project, I have implemented the following code snippet to create a select interaction, integrate it into a map, and execute certain actions when a feature is selected or deselected: const select: Select = new Select({ hitTolerance: 5, mul ...

Can an @ArgType be created using a combination of union @Field?

Is there a way for me to define an input / argtype that could either be a ContactCreate or a ContactRead? I'm uncertain if it's feasible. import { ContactCreate } from 'contact/ContactCreate' import { ContactRead } from 'contact/C ...

What is the importance of including "declare var angular" while working with Typescript and AngularJS?

I've been working on an AngularJS 1.7 application that's coded entirely in TypeScript, and there's always been one thing bothering me. Within my app.module.ts file, I have this piece of code that doesn't sit right with me: declare va ...

Installing express in typings: A beginner's guide

I'm currently incorporating expressjs into my application. Following its installation using typings install express --ambient --save, I run tsc. However, two errors are returned: typings/main/ambient/express/index.d.ts(17,34): error TS2307: Unable ...

What is the best way to adjust the THREE.js view to match the size of the canvas

I am trying to implement dragging and dropping an element to the mouse position by synchronizing THREE.js space with the canvas size, where the coordinates {0,0} are located at the center of the screen. If my element has a width of 500px, dropping it at a ...

Employing square bracket notation based on the input data

I'm currently in the process of enhancing some code within my library, but I've encountered a perplexing issue with bracket notation not functioning as expected when attempting to call an imported class. The parameter type expects a camelCased s ...

Tips for expanding a React class using typescript

Let's consider a basic example: interface BaseProps { name: string; } class BaseClass<P extends BaseProps> extends React.Component<P, void> { } interface SuperProps { } class SuperClass extends BaseClass<SuperProps> { } M ...

What is the best approach for organizing type declaration files when using Vite in a React TypeScript application?

Currently in the process of transitioning my application from Webpack to Vite. Seeking guidance on the optimal method for including the following: /// <reference types="vite/client" /> There is an existing file types/global.d.ts, and I&ap ...

What is the method for including Location in a function within the NgModule declaration?

Below is the code snippet I am working with: @NgModule({ imports: [ .. TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: (createTranslateLoader), deps: [HttpClient] ...

employing a parameterized type to accommodate a combination of two diverse items as input

I am facing a challenge with a simple use case and have been unable to find an example that covers it adequately. The situation is this: I have a function that should accept two different objects that are very similar. Therefore, I want to use a generic t ...

Steps to fix: "Rule '@typescript-eslint/consistent-type-assertions' is missing a definition"

My React app is failing to compile because it can't find the rule definition for '@typescript-eslint/consistent-type-assertions'. I'm feeling quite lost at the moment. I can't seem to locate any current rule definitions within the ...

Order Typescript by Segment / Category

Suppose we start with this original array of objects: {vendor:"vendor1", item:"item1", price:1100, rank:0}, {vendor:"vendor1", item:"item2",price:3200, rank:0}, {vendor:"vendor1", item:"item3", price:1100, rank:0}, {vendor:"vendor2", item:"item1", price: ...

Issue with Angular/Jasmine: Undefined property 'pipe' not readable

I have been struggling to resolve a problem with Angular 9, Jasmine, and RxJS without much success. While my unit tests run successfully in Jasmine, there are certain lines of code that do not get executed. Despite scouring multiple posts for assistance, ...

Execute a selector on child elements using cheerio

I am struggling to apply selectors to elements in cheerio (version 1.0.0-rc.3). Attempting to use find() results in an error. const xmlText = ` <table> <tr><td>Foo</td><td/></tr> <tr><td>1,2,3</td> ...

Dealing with Exceptions in NestJS and TypeORM: Troubleshooting "Get Status is not a function"

Currently, I am working on an application utilizing NestJS and TypeORM. My main objective is to handle TypeORM errors by using exception filters. However, I have run into a roadblock as I am facing this particular error: (node:345) UnhandledPromiseReject ...

Is anyone able to assist with resolving the problem of `tsc` constantly monitoring `node_modules`?

Using the Expo platform has been a great experience for me. Here is a snippet from my tsconfig.json: { "compilerOptions": { "paths": { "@/*": [ "./src/*" ], ...

Creating global variables in NodeJS allows you to access and modify data

Currently, this construct is being utilized to create a global LOG: declare global { let LOG: Logger; } // eslint-disable-next-line @typescript-eslint/no-namespace declare namespace globalThis { let LOG: Logger; } globalThis.LOG = new Logger(); It f ...

Exploring Substrings in TypeScript strings

Can you pass a partial string (substring) as a value to a function in TypeScript? Is something like this allowed? function transform( str: Substring<'Hello world'> ) { // ... } If I call the function, can I pass a substring of that st ...