Is it possible for TypeScript to automatically determine a function's return type by analyzing an argument in this particular situation?

Currently, I am in the process of developing a function that retrieves data from an API. Having a clear idea of what the data will be according to the API path, I opted for using 'ApiSignature' types.

Here is a simplified version of my code:

interface Order {
    id: string
}

type ApiSignature<P, D> = {
  path: P,
  data: D
};

type OrdersApiSignature = ApiSignature<'/orders', Order[]>
type OrderApiSignature = ApiSignature<'/order', Order>

type AnyApiSignature = OrdersApiSignature | OrderApiSignature

function useApi<T extends AnyApiSignature>(path: T['path']) {
    return null as any as T['data']
}

const data = useApi('/orders') // Order | Order[]

Playground link

Could TypeScript potentially infer that in this scenario the data will be Order[] rather than just Order, based on the function argument?

Answer №1

Expanding upon the point raised by @jcalz,

If your path is stored as a string, you can handle it more traditionally by utilizing key lookup similar to this example.

type ApiSignatureMap = {
  "/orders": Order[];
  "/order": Order;
};

type AnyApiSignature = {
  [K in keyof ApiSignatureMap]: ApiSignature<K, ApiSignatureMap[K]>;
}[keyof ApiSignatureMap];

function useApi<P extends AnyApiSignature["path"]>(path: P): ApiSignatureMap[P] { ... }

A mapping approach is adopted to assign paths with their corresponding data types (ApiSignatureMap).

To encompass all potential API signatures, we employ a mapped type:

{
  [K in keyof ApiSignatureMap]: ApiSignature<K, ApiSignatureMap[K]>;
}

This will yield an object containing all desired API signatures. To merge them into a union, we utilize keyof ApiSignatureMap:

{
  [K in keyof ApiSignatureMap]: ApiSignature<K, ApiSignatureMap[K]>;
}[keyof ApiSignatureMap]

Finally, within the function declaration, a generic P is utilized for any valid path in AnyApiSignature, followed by fetching the return type using the aforementioned API signature map.

Check it out here!

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

What is the correct way to create a new SystemError object in Node.js?

After studying Node's documentation, it is evident that SystemError is an extension of the Error class. However, when attempting to utilize SystemError directly, Node throws a ReferenceError indicating that SystemError is undefined. While it is theor ...

An Easy Guide to Incorporating react-cookie into TypeScript Projects

I am currently developing an application in React using the React template provided by Visual Studio 2017. My goal is to incorporate react-cookie into my project. After installing this library with the command: npm install react-cookie However, when I at ...

What is the proper way to create a child JSON object in Angular using TypeScript?

I have a model.ts file. export class MainClass { someVar: string; var2: string; subClass: SubClass; contact: ContactList; } export class SubClass { subItem: { item1: string; item2: string; item3: string; } constructor() ...

What could be causing my object property to be undefined when used in an if statement?

My attempt to search through an array using a for loop is not yielding the expected results. let matrix = []; for(let i=0; i<this.row; i++){ for(let j=0; j<this.column; j++){ if(this.grid[i][j].name != ""){ ...

Utilizing Service within Express Router

My journey into the world of NodeJS is just beginning, coming from a .NET background with a love for dependency injection, inversion of control, and microservices. I am now venturing into TypeScript to create services based on my past experiences. Using ex ...

Optimal asset management strategies for Angular applications

What is the process for loading assets in an Angular app? Will the app wait for all assets to load before bootstrapping, or will they be lazy loaded if not needed on the initial page? I have a large number of PDFs stored in the assets folder that I load a ...

Angular 6 encounters issues when making Http requests with authorization headers

Currently, I am in the process of deploying an application developed using Angular 6 that interacts with a Tomcat server on localhost. Everything runs smoothly until I introduce a header field into my HTTP request. this.http .post<LoginResult> ...

React throwing a typescript error while attempting to update state based on the previous state

Hello there! I'm fairly new to working with TypeScript and I've encountered an issue with a piece of state in a child component. I'm trying to modify it based on the previous value, but every time I call the setState function, I get a type e ...

Leveraging JSON.stringify alongside getter/setter in TypeScript

In my TypeScript code, I am utilizing getter/setter accessors. To differentiate between variables and methods with the same name, I have adopted the convention of prefixing the variable with a lower dash, as shown in many examples: private _major: number; ...

Using "undefined" as the discriminator in a discriminated union

My idea is to implement a discriminated union using null as the discriminator: type Result<T> = { result: T; error: null } | { result: null; error: string } If the function register() returns a Result, I could handle it like this: const { error, res ...

An error may occur when Typescript is instantiated with a varying subtype of constraint

I am encountering the "could be instantiated with a different subtype of constraint" error when trying to return a result that should match the expected type of a function. Despite removing irrelevant details, I'm struggling to pinpoint what exactly I ...

Create a new instance of the parent class in TypeScript to achieve class inheritance

Looking for a solution to extending a base class Collection in JavaScript/TypeScript to handle domain-specific use cases by implementing a "destructing" method like filter that returns a new instance with filtered elements. In PHP, you can achieve this usi ...

The JestImportMeta interface is mistakenly extending the ImportMeta interface, causing an error

While transitioning from jest version 27 to v29, I encountered this issue: node_modules/@jest/environment/build/index.d.ts:329:26 - error TS2430: Interface 'JestImportMeta' improperly extends interface 'ImportMeta'. The types returned ...

Caution: The absence of FIREBASE_CONFIG and GCLOUD_PROJECT environment variables may result in the failure to initialize firebase-admin

I followed a tutorial to set up the Firebase Admin SDK. https://firebase.google.com/docs/admin/setup I downloaded a JSON file (service account) from the Firebase console located at: C:\ct\functions\src\cargo-tender-firebase-adminsdk- ...

Issue with the Material UI theme module enhancement feature not functioning as expected

I've been researching the MUI documentation, blogs, and various posts on Stackoverflow, but despite my efforts, I can't seem to get my vscode intellisense/typescript to recognize the changes I've made. These are fairly straightforward modif ...

The timezone plugin in day.js may sometimes generate an incorrect date

For a while, I've been using dayjs in my angular project to convert timestamps from UTC to localtime. However, after my recent update, this functionality stopped working. This isn't the first issue I've encountered with dayjs, so I decided t ...

A guide on implementing RxJS Observables to target a specific DIV element

Currently, I am working with Angular 2. At the moment, I have been using this method to select a specific DIV element: <div #aaa> </div> @ViewChild('aaa') private aaa: ElementRef; ngAfterViewInit() { let item = this.aaa.nativeEle ...

Leveraging Angular2+ components across various modules

Bringing in a component called "TemperatureComponent" into a module named "DashboardModule" and defining + exporting it there: import { TemperatureComponent } from './temperature/temperature.component'; import { TemperatureDraggerComponent } from ...

What is the best way to implement a timer using hooks in React?

Just getting started with React! I began my journey last week ;) My first task is to build a timer that includes a reset feature and can count seconds. While the reset function is functioning properly, the timer isn't. Can anyone suggest the best ap ...

Combining React Conetxt with useReducer and Typescript throws the error message: 'No overload matches this call'

Here is a sample of my reducer code: import { IState } from "./initialState"; import { TAction } from "./actions"; import * as types from './types'; const myReducer = (state: IState, action: TAction): IState => { const ...