The returned type of intersected functions in Typescript does not match the inferred type

While attempting to extract the return type of an intersected request, I encountered a discrepancy between the return type and the inferred type. Check out the shortened URL for more details: https://tsplay.dev/mAxZZN

export {}
type Foo =  (() => Promise<string>) & (() => Promise<any>) ;


type FooResult = Foo extends () => Promise<infer T> ? T : null
//   ^?

const a:Foo = async () => {
        return "";
    }

const b = await a();
//    ^?


type Foo2 =  (() => Promise<any>) & (() => Promise<string>);

type FooResult2 = Foo2 extends () => Promise<infer T> ? T : null
//   ^?

const c:Foo2 = async () => {
    return "";
}

const d = await c();
//    ^?

Both examples above show mismatched results with FooResult:any b:string and FooResult2:string d:any

To clarify, instead of using a Foo type, I now have an intersected type like

HTTPRequest & {json: () => Promise<Type>}
to ensure the correct return type of the request json object.

Is there a way to align these two examples correctly to the same type? If so, could someone provide guidance on how to achieve this? Thank you in advance for your help! <3

Answer №1

When dealing with intersection function types in TypeScript, it's important to note that they are essentially equivalent to overloaded function types with multiple call signatures. However, using multiple call signatures in such cases can lead to limitations at the type level. If you find yourself facing such limitations and do not have a critical need for overloads, it's recommended to refactor your code to use a single call signature instead.


For overloaded functions, the call is resolved by selecting "the most appropriate" call signature, usually the first one in the ordered list that matches the input:

// call signatures
function foo(x: string): number;
function foo(x: number): string;

// implementation
function foo(x: string | number) {
    return typeof x === "string" ? x.length : x.toFixed(1)
}

const n = foo("abc"); // resolves to first call signature
// const n: number

const s = foo(123); // resolves to second call signature
// const s: string

Therefore, the return type is determined by the input type.


On the other hand, when inferring from an overloaded function type, TypeScript primarily infers from the last call signature:

type FooRet = ReturnType<typeof foo>
// type FooRet = string
// ^^^^^^^^^^^^^^^^^^^^ not (string & number) or [string, number]

This behavior is highlighted in the TypeScript Handbook documentation and is considered a design limitation of the language.

While there are potential workarounds using conditional types to handle multiple call signatures, they are delicate and should be approached with caution based on your specific use case.


In scenarios where you have two call signatures with the same parameter types, they may not behave as expected:

function bar(): { a: string };
function bar(): { b: number };

// implementation
function bar() {
    return { a: "", b: 1 }
}

Calling the function will return the result based on the first call signature:

const a = bar();
// const a: { a: string; }

However, inferring from the function type will result in the last call signature being used:

type BarRet = ReturnType<typeof bar>;
// type BarRet = { b: number; }

In such cases, it's advisable to consolidate the multiple call signatures into a single call signature that reflects the desired return type.


In conclusion, it's beneficial to simplify your overloaded function types by reevaluating the need for multiple call signatures and opting for a cleaner, single call signature approach instead.

If you wish to explore further or experiment with code examples, you can access the TypeScript Playground link provided.

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

Recursively toggle child elements using JQuery

My knowledge of JQuery is limited, so here's the issue I'm facing. Below is an example of the HTML structure: <ul class="first-class"> <li class="second-class"> <a href="#_"> <i class="fa fa-plus"></i& ...

Prevent form submission from refreshing the page by using jQuery and ajax

I'm looking to submit a form without refreshing the page, but my current script isn't working as expected: $('#formId').submit(function(e){ $.ajax({ type: "POST", url: 'serverSide.php', data: $(&ap ...

What is the best way to link CSS files from libraries that are downloaded using npm?

For instance, let's say I installed a package: npm install --save package and it gets saved in ./node_modules/package/ Inside that folder, there might be a directory named styles and within that directory, you could find style.css. How can I link ...

Could you display the picture prior to the function commencing?

This is the image that needs to be loaded before the loop begins. <div id="attendenceGridDivLoader" style="display:none"> <img src="<?php echo base_url() . 'images/loader.gif'; ?>" /> </div> <select onchange=checkAll ...

Incorporate an HTML code string into an Angular 2 template

I am working with HTML code stored in a Component variable, shown below: import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: `Hi <div [innerHTML]="name"></div>`, styleUrls: [' ...

What is the best way to update the state of a different component?

Snippet: var React = require('react'); var RecipeBox = require('./RecipeBox.jsx'); var AddRecipe = React.createClass({ handleClick: function () { RecipeBox.setState({ adding: false }); }, rend ...

Error: The expression `xmlDoc.load` returns a value of undefined, which is not an object

My current challenge involves loading an XML file using Javascript in IE, Firefox, and Safari. I have yet to find a function that works well across all three browsers. The load function I am currently using is similar to the one found in w3schools tutorial ...

Troubleshooting: How to resolve the issue of receiving undefined values when passing API data to a child component with Axios in React

I am encountering difficulties in retrieving data that I pass to a child component. The issue may be related to the Promise not being resolved at the time of access, but I am unsure how to address it. My goal is to ensure that the data is fully retrieved f ...

What is the best way to execute my node script with a shell script file?

Currently, I'm working on a personal website using node to help me organize the ebooks I'm reading. Right now, my process involves moving to the project folder and running node server.js. I had an idea to create a shell script so that all I hav ...

Unable to host Express and React application on port 80

I have a React application compiled with Express serving as a static React site, and I want to host them on port 80. The challenge is that my VPS runs Ubuntu with Plesk Onyx supporting multiple applications as subdomains on vhosts using port 80: server.l ...

There seems to be an issue with the import class for React PropTypes. The prop

I have multiple oversized items that are utilized in numerous components, so I created a PropTypes file for each item. For example: PropTypes/PropLargeObject.js This file contains: import PropTypes from "prop-types"; const PropLargeObject = Prop ...

JavaScript's Ajax request seems to be stagnant and inactive

Having some difficulties with the code below. It triggers an alert correctly, but the ajax part doesn't seem to be functioning. No errors or indications of what's wrong. $(document).on('change', '.department_select', function ...

Launching a Node.js Express application on Heroku

I'm facing an issue while trying to deploy my app on Heroku, as I keep encountering the following error: 2022-08-11T12:49:12.131468+00:00 app[web.1]: Error: connect ECONNREFUSED 127.0.0.1:3306 2022-08-11T12:49:12.131469+00:00 app[web.1]: at TCPConnect ...

Why opt for ref.current over directly applying the condition?

I'm curious as to why the code uses if (ref.current && !ref.current.contains(event.target)) rather than if (!ref.current.contains(event.target) function useOutsideAlerter(ref) { useEffect(() => { // Function for click event function hand ...

Establishing a Goal in Google Analytics using JavaScript

My approach to recording Goals in Google Analytic tool via JS involves adding code directly to the page: <meta name="description" content="iReserve"> <link rel="shortcut icon" href="/ico/favicon.ico" /> & ...

What are the steps to resolve the issue of assigning void type to type ((event: MouseEvent<HTMLDivElement, MouseEvent>) => void) | undefined in a react application?

I'm trying to update the state isOpen to true or false when clicking on a div element, but I keep getting an error with the following code: function Parent() { const [isOpen, setIsOpen] = React.useState(false); return ( <Wrapper> ...

What is the best way to scale down my entire webpage to 65%?

Everything looks great on my 1920x1080 monitor, but when I switch to a 1024x768 monitor, the content on my webpage becomes too large. I've been manually resizing with CTRL+Scroll to reduce it to 65%, which works fine. Is there a code solution using CS ...

What steps should I follow to create a JavaScript file incorporating jQuery?

As a newcomer to JavaScript and JQuery, I come from a background in basic C++ where I enjoy including header files and calling functions from there to maintain clean code. Now that I want to create a new JavaScript file, how can I ensure that I am able to ...

Have you attempted to configure a JSON file to facilitate language translations?

I need some guidance on structuring my data.json file efficiently. Currently, it is set up as shown in the example below. The goal is to have a drop-down menu where users can select a language and then display 50 different pages with specific content. I wa ...

Challenges encountered while using Selenium WebDriver to upload images

I'm having trouble adding an image as a normal attachment to an email (not as an inline image). When inspecting the HTML DOM with Firebug, I noticed there are two elements with xpath //input@type='file', which is due to the presence of two b ...