Determine the return type of a function in Typescript based on the input arguments

Currently, I am attempting to write a function that accepts an optional transformResult. My challenge lies in correctly typing the useQuery function to achieve the following:

  • If transformResult is not provided, it should return a type of QResult.
  • If the callback is provided, it should return TResult (or the return type of the transformResult callback, if possible).

How can this be accomplished? While I am open to using function overrides for an easy solution, I prefer conditional types.

Thank you in advance for your help!

type QueryOptions<QResult, TResult> = {
    transformResult?: (queryResult: QResult) => TResult;
    // Other options that are not so important
    enableDebug?: boolean;
};

function useQuery<QResult, TResult = QResult>(
    query: string,
    options?: QueryOptions<QResult, TResult>,
) {
    const queryResult = doSomeServerStuff<QResult>(query);
    if (options?.transformResult != null) {
        return options.transformResult(queryResult);
    } else {
        return queryResult;
    }
}

function doSomeServerStuff<Q>(query: string): Q {
    /* Not so important just to make it compile */
    return {} as Q;
}

//typeOf 'number' <- Good
const result = useQuery<number>('q');

// Expecting 'boolean' type, but 'number | boolean' is actual
const transformedResult = useQuery<number, boolean>('q', {
    transformResult: (qResult) => qResult != null,
});

Access the TypeScript playground here.

Answer №1

Utilizing function overloads is key in situations like this. When the return type of a function varies based on the type of its argument, function overloads provide a straightforward way to handle it.

To begin with, organizing the types as follows can be helpful:

interface QueryOptionsBase {
    // Other irrelevant options
    enableDebug?: boolean;
}

interface QueryOptionsWithoutTransform extends QueryOptionsBase {
    transformResult?: undefined;
};

interface QueryOptionsWithTransform<QResult, TResult> extends QueryOptionsBase {
    transformResult: (queryResult: QResult) => TResult;
};

type QueryOptions<QResult, TResult> = QueryOptionsWithoutTransform | QueryOptionsWithTransform<QResult, TResult>

QueryOptionsBase defines the properties common to all pathways.

QueryOptionsWithoutTransform represents the branch without any transformation.

QueryOptionsWithTransform deals with the transformation branch.

QueryOptions combines both branches into a union.

Then defining the overloads can be done as shown below:

function useQuery<QResult>(query: string, options?: QueryOptionsWithoutTransform): QResult
function useQuery<QResult, TResult = QResult>(query: string, options: QueryOptionsWithTransform<QResult, TResult>): TResult

function useQuery<QResult, TResult = QResult>(
    query: string,
    options?: QueryOptions<QResult, TResult>,
) {
    const queryResult = doSomeServerStuff<QResult>(query);
    if (options?.transformResult != null) {
        return options.transformResult(queryResult);
    } else {
        return queryResult;
    }
}

This setup functions as expected:

// 'boolean' type
const transformedResult = useQuery<number, boolean>('q', {
    transformResult: (qResult) => qResult != null,
});

And also supports cases like:

// '{ foo: string; }' type
const transformedResultInferred = useQuery('q', {
    transformResult: (qResult) => { foo: 'bar' }),
});

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

Utilize ngFor in Angular Ionic to dynamically highlight rows based on specific criteria

I'm working on an application where I need to highlight rows based on a count value using ngFor in Angular. After trying different approaches, I was only able to highlight the specific row based on my count. Can someone please assist me? Check out m ...

Creating a table with merged (colspan or rowspan) cells in HTML

Looking for assistance in creating an HTML table with a specific structure. Any help is appreciated! Thank you! https://i.stack.imgur.com/GVfhs.png Edit : [[Added the headers to table]].We need to develop this table within an Angular 9 application using T ...

TypeScript type declaration for promise.prototype.finally signature

I recently came across a ES6 Promise compatible finally implementation called promise.prototype.finally that I was using in a Node application. Now, I want to convert this application to TypeScript but unfortunately, I couldn't find any typings availa ...

Retrieve data from a MessageEvent's properties

I have developed an Angular 6 frontend application that uses Websockets to communicate with the backend server. Upon sending a login request, the backend responds with a JSON object: {"type":"LoggedIn","value":{"email":"<a href="/cdn-cgi/l/email-prote ...

Solving issues with props types in React and TypeScript

Setup for Development ・react ・typescript ・styled-components Inquiry The variable open is being transfered from the parent component to the child component as shown below. <RightNav open={open}/> An issue arises within the child compon ...

Invalid file name detected during the download process

Below is the Javascript code I currently use to download a pdf: var link = document.createElement('a'); link.innerHTML = 'Download PDF file'; link.download = "Report.pdf"; link.href = 'data:application/octet-stream;base64 ...

Tips on how to effectively unit test error scenarios when creating a DOM element using Angular

I designed a feature to insert a canonical tag. Here is the code for the feature: createLinkForCanonicalURL(tagData) { try { if (!tagData) { return; } const link: HTMLLinkElement = this.dom.createElement('link'); ...

Prisma causing a compiler error

Currently, I am in the process of developing a project that integrates a GraphQL server and utilizes Prisma to establish a connection with the database. Additionally, I have chosen to implement this project using TypeScript. Unfortunately, as I compile my ...

The React component was not able to receive any children as input

My Typescript-written React component is called GradientText.tsx: import React, { ReactNode } from "react" import { cn } from "@/lib/utils" const typeMap = { h1: "h1", h2: "h2", p: "p", } inte ...

Retrieving information using React Query in TypeScript

When working with React Query and TypeScript, I encountered an issue with the getRecommendations hook. I wanted to only send the latest recommendations (I may create another hook for watchlist items in the future). The error I received was related to the q ...

What could be causing the reoccurrence of the error message "JSX is not defined no-undef"?

Encountering an issue while adding a carousel to my home page, I stumbled upon the following error: 'JSX' is not defined. Despite searching on Stack Overflow, GitHub, and Google, the answers provided are similar but I am still unsure of what m ...

Encountering a TypeScript error when attempting to utilize indexOf on a Typed Array, leading to restriction

I have been working with an Interface, where I created an array of type Interface. I am currently facing some IDE error complaints when trying to use the .indexOf method on the Array. These errors seem confusing to me, and I'm hoping someone here migh ...

I am struggling to get the mat-paginator to function correctly as it seems that all the data is being

I'm facing an issue while trying to implement pagination using mat-paginator in Angular for my table. Despite following the instructions from the Angular documentation, the entire table is getting loaded on the first page. I attempted the following s ...

Dealing with Nodejs promises can be frustrating, especially when encountering errors, but incorporating the Sequelize ORM can

Currently I am working on developing a web application for nodejs using the popular sequelize ORM and typescript. Here is an example from my code: this.createNewGame(player.idPlayer).then((game) => { this.getBestScore(player.idPlayer).then((bestSc ...

What is the most efficient method for sorting a complex JSON object?

I have a JSON object with multiple nested levels: { "myJson": { "firstGroup": { "0": [ { "month": 1.0, "amount": 1.7791170955479318, ...

Top location for securely storing information in Angular 8

I have developed a web application using Angular 8. My goal is to secure routes and pages with dynamic access levels. For instance, I want to verify if a user has access to a specific route, and if not, redirect them to the login page. To do this, I cur ...

Another option to avoid using complicated else if chains

I'm facing a challenge with a function that returns a value known as user_id. It involves multiple conditions that need to be checked. First condition: Verify the service variable Second condition: If not found, retrieve user_id from local storage ...

Determine the data type of an object key based on the value of another key within the same

Consider having an Object Type named Options, which consists of two keys: strategy: a function that takes one parameter of an unknown type parameter: the same type as the first argument of strategy The desired functionality is as follows: type FooParam ...

How to integrate half-star ratings in Ionic

Recently, I have been struggling with implementing half stars in the rating module of my Ionic mobile application. When the average rating is not a whole number, I opted to round it off and display an Ionic icon star. Below are snippets of the code I curre ...

Issue with MUI Dialog: Struggling to pass the onClose function to onClick within dialog actions

Currently, I am in the process of developing a reusable component called Dialog which is based on MUI dialog. Below is the code snippet for this component: import React from 'react'; import { Dialog as MuiDialog, DialogProps, Button, Dia ...