Typescript is unable to interpret an anticipated overloaded function signature

I'm currently facing challenges with TypeScript overload resolution.

When using the googleapis library with TypeScript to fetch all tag manager accounts records, the list function requires pagination if the response body contains a nextPageToken. I aim to create a function that can paginate through all records and retrieve them.

The idea is to create a curried function like this - it takes the list function as an argument and continues calling it with the nextPageToken until no more tokens are included in the returned data.

// Example 1)
const allAccounts = await paginateAll(tagmanager.accounts.list)({
  // specify tagmanager.accounts.list parameters
});

// Example 2)
const allContainers = await paginateAll(tagmanager.accounts.containers.list)({
  // specify tagmanager.accounts.containers.list parameters
});

I defined a signature for paginateAll as shown below, but TypeScript seems unable to resolve the appropriate overload.

export const paginateAll = <P1, P2, R>(
  list: (params?: P1, options?: P2) => Promise<R>,
): ((arg1: P1, arg2: P2) => Promise<Array<R>>) => async (a, b) => {
    // some procedure...
  };

const fetchAllAccounts = paginateAll(tagmanager.accounts.list)
                                     ^^^

=== ERROR ===
Argument of type '{ (params: Params$Resource$Accounts$List, options: StreamMethodOptions): GaxiosPromise<Readable>; (params?: Params$Resource$Accounts$List | undefined, options?: MethodOptions | undefined): GaxiosPromise<...>; (params: Params$Resource$Accounts$List, options: StreamMethodOptions | BodyResponseCallback<...>, callback: ...' is not assignable to parameter of type '(params?: BodyResponseCallback<Schema$ListAccountsResponse> | undefined, options?: unknown) => Promise<unknown>'.
  Types of parameters 'params' and 'params' are incompatible.
    Type 'BodyResponseCallback<Schema$ListAccountsResponse> | undefined' is not assignable to type 'Params$Resource$Accounts$List'.
      Type 'undefined' is not assignable to type 'Params$Resource$Accounts$List'.

The list function in googleapis has 6 overloads, and I expect paginateAll to select the 2nd signature.

1. list(params: Params$Resource$Accounts$List, options: StreamMethodOptions): GaxiosPromise<Readable>;
2. list(params?: Params$Resource$Accounts$List, options?: MethodOptions): GaxiosPromise<Schema$ListAccountsResponse>;
3. list(params: Params$Resource$Accounts$List, options: StreamMethodOptions | BodyResponseCallback<Readable>, callback: void;
4. list(params: Params$Resource$Accounts$List, options: MethodOptions | BodyResponseCallback<Schema$ListAccountsResponse>, callback: void;
5. list(params: Params$Resource$Accounts$List, callback: BodyResponseCallback<Schema$ListAccountsResponse>): void;
6. list(callback: BodyResponseCallback<Schema$ListAccountsResponse>): void;

I would greatly appreciate any insights on why this issue is occurring...

==== UPDATE ====

I have replicated the error from my question using TypeScript Playground. (I've simplified it by removing the curry aspect)

type Params = {
    value1: string;
    value2: string;
}

type Options1 = {
    option1Value: string;
};

type Options2 = {
    option2Value: string;
};

type Resonse1 = {
    response1Value: string;
}

type Response2 = {
    response2Value: string;
}

type Callback<T> = () => T

declare function func(params: Params, options: Options1): Promise<Resonse1>;
declare function func(params?: Params, options?: Options2): Promise<Response2>;
declare function func(params: Params, options: Options1 | Callback<Resonse1>, callback: void;
declare function func(params: Params, options: Options2 | Callback<Response2>, callback: void;
declare function func(params: Params, callback: Callback<Response2>): void;
declare function func(callback: Callback<Response2>): void;

const anotherFunc = async <P1, P2, R>(
  fn: (params?: P1, options?: P2) => Promise<R>,
): Promise<R> => {
    return fn();
}

const test = anotherFunc(func);

Answer №1

The compiler struggles to choose an overload unless the overloaded function is directly called with inputs. If you pass the overloaded function to another function and rely on generic type inference, the compiler may not resolve overloads as expected. It often just selects a default overload, usually the first or last one available. This limitation is inherent in TypeScript's design, refer to microsoft/TypeScript#30369 for more details.

To address this issue, it is necessary for you to explicitly select the desired signature. Here's one approach:

const f: (params?: Params, options?: Options2) => Promise<Response2> = func;
const test = anotherFunc(f);

In this example, we assign the function func to a variable f, specifying only the call signature we intend to infer. This assignment effectively eliminates any overloads within f, allowing the call to anotherFunc(f) to behave as intended.

Playground link to code

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

openapi-generator is generating subpar api documentation for TypeScript

Executing the command below to generate an auto-generated API using openapi-generator (v6.0.1 - stable): openapi-generator-cli generate -i app.json -g typescript -o src/main/api The json file is valid. Validation was done using openapi-generator-cli valid ...

Parsing values from deeply nested objects and arrays

I've come across this issue before, but I'm having difficulty navigating through a nested structure. I can't seem to find any guidance in the right direction. Here is the object I'm attempting to parse: const nestedArray = { id ...

Dealing with TypeScript errors TS2082 and TS2087 when trying to assign an Away3D canvas to a variable

As a beginner with TypeScript, I am in the process of creating an Away3D scene where a canvas element is dynamically generated and needs to be appended to a container. Although the code functions correctly, I am encountering the following compilation erro ...

What is the process for transforming a TypeScript Node.js project into a standalone .exe executable file?

Currently, I am in the process of compiling my TypeScript project into JavaScript to eventually convert it into an executable file. I have experimented with various tools like https://github.com/nexe/nexe, https://github.com/vercel/pkg, and . My usage of ...

Is it better to store data individually in localStorage or combine it into one big string?

When it comes to keeping track of multiple tallies in localStorage, one question arises: Is it more efficient to store and retrieve several small data points individually or as one larger chunk? For example: localStorage.setItem('id1', tally1); ...

Creating types for React.ComponentType<P> in Material-UI using TypeScript

I am currently working with Typescript and incorporating Material-UI into my project. I am trying to define the component type for a variable as shown below: import MoreVert from '@material-ui/icons/MoreVert' import { SvgIconProps } from '@ ...

Tips for defining a key: reducerFunctions object within a Typescript interface

Exploring the given interface: interface TestState { a: number; b: string; } My goal is to create a generic type that enforces an object to: have the same keys as a specified interface (e.g. TestState) for each key, provide a value of a reducer funct ...

Angular's implementation of a web socket connection

I am facing an issue with my Angular project where the web socket connection only opens upon page reload, and not when initially accessed. My goal is to have the socket start as soon as a user logs in, and close when they log out. Here is the custom socke ...

Upon running the code, no errors appear on the console. However, my project isn't functioning properly and I'm encountering errors on the web console

ReferenceError: require is not defined when trying to access external "url" at Object.url in webpack_require (bootstrap:83) at client:6 importing from webpack-dev-server client index.js(http://0.0.0.0:0) vendor.js:219506 dynamically imp ...

Typescript indicates that an object may be potentially null

I've hit a roadblock where I keep getting warnings that the objects might be null. After searching online and on StackOverflow, I've tried numerous solutions with no luck. My goal is to insert the text "test" into the HTML elements using their ID ...

npm ERROR: The start script for [email protected] has encountered a failure with the commands 'tsc && concurrently "tsc -w" "lite-server"'

I followed a tutorial to make changes to the app/main.ts file and now I am encountering errors when I try to run "npm start": Here is the project file with the commit message error: https://github.com/monajalal/angular2_projects import {bootstrap} fro ...

How to send Multipart form data with a string payload

Many suggestions in regards to this issue recommend utilizing some form of FormData within nodejs for building a multipart form. However, I am seeking to achieve the same result without relying on the FormData library. Instead, I aim to use only request h ...

How can I incorporate the LIKE operator in a query when dealing with PostgreSQL's String array type using TypeORM?

My database backend is PostgreSQL and I have a TypeORM object simplified as follows: @Entity() @Index(['name'], {unique: true}) export class Foo extends BaseEntity { @PrimaryGeneratedColumn('uuid') id: string; @Column() name: st ...

Make sure that each function within a generic interface is asynchronous

Imagine having an abstract class that accepts a generic type export abstract class RegisterableClass<InstanceType> and a class that implements it like this: class UserService extends RegisterableClass<IUserService> implements IUserService { ...

ADAL-Node: Unable to locate tenant groups

When the authority URL is similar to (where the domain name belongs to your tenant), an error occurs: The Get Token request returned an HTTP error: 400 with the server response stating "error description AADSTS90002 Tenant 'organizations' not ...

Guide to transforming an IdentityMap<String, dynamic> into a UInt8List

I have a cloud function that generates a JavaScript Buffer object, which looks something like this: functions .region("europe-west2") .runWith({ timeoutSeconds: 20, memory: "128MB", }) .https .onCall(asyn ...

ReactNative: When attempting to navigate, a TypeError occurred - this.props.navigation.navigate is not a function

It's confusing to see how this issue is occurring, even though all props have been properly typed. The goal is to pass the navigator to the bottom bar component in order to navigate onPress. Initially, I define the props interface: export interface B ...

What steps should I take to create a React component in Typescript that mimics the functionality of a traditional "if" statement?

I created a basic <If /> function component with React: import React, { ReactElement } from "react"; interface Props { condition: boolean; comment?: any; } export function If(props: React.PropsWithChildren<Props>): ReactElement | nul ...

Performing simultaneous document queries within a single API in MongoDB

I am currently working with an API written in typescript and attempting to execute parallel queries for the same document by using Promise.allSettled. However, I have noticed that it is performing poorly and seems to be running sequentially instead of in p ...

An error occurred when attempting to access the property 'push' of an undefined value while processing the SafeSubscriber._next function

There was an issue: Cannot read property 'push' of undefined at SafeSubscriber._next import { Component, OnInit } from '@angular/core'; import {StudentService} from '../student.service'; import {student} from '../ ...