How to change the return type of a method with multiple overloads in TypeScript

I am currently developing an open-source library that creates Proxies for Rx Observables.

(For the sake of simplicity, I will use Box<A> instead of Observable<A>)

The library takes a Box<A> as input and returns a type

{ [P in keyof A]: Box<A[P]> }
.

There is one important detail: all methods on this new type must also return a Box of the method result while maintaining their parameter types. For example:

Box<{ m(a:string):any }>
// becomes
{ m(a:string):Box<any> }

However, I am encountering an issue when attempting to proxy A which has overloaded methods. The problem lies in redefining only one individual method definition within the overload group.

To simplify the issue:

type Proxy<O> = { [P in keyof O]: Wrap<O[P]> }

type Wrap<O> = O extends (...a: infer P) => infer R
  ? (...args: P) => Proxy<R>
  : Proxy<O>

class A {
  m(a: number): any;
  m(a: string): any;
  m(...a:any[]) {}
}

let p = {} as Proxy<A>;
// p.m resolves to a single override
// m(a: string): void;
p.m('1') // > returns Proxy<any>
p.m(1)   // > Error: 1 is not string

Try this snippet in TS playground

Is it even possible?

Any assistance with resolving these typing issues would be greatly appreciated. Thank you!

Notes:

Everything works perfectly without typings!

You can find the library here: rxjs-proxify (types defined in src/proxify.ts)

Answer №1

One challenge arises when dealing with overloaded functions in mapping. Simply using

O extends (...a: infer P) => infer R
will only return the last overload. Unfortunately, there is no direct way to retrieve arguments for multiple overloads, but you can create a conditional type to obtain the overloaded arguments up to a specific number of overloads by following the technique described here

type Proxy<O> = { [P in keyof O]: OverloadedWrap<O[P]> }

type OverloadedWrap<T> =
    T extends
        { (...args: infer A1): infer R1; (...args: infer A2): infer R2; (...args: infer A3): infer R3; (...args: infer A4): infer R4 } ? 
        { (...args: A1): Proxy<R1>; (...args: A2): Proxy<R2>; (...args: A3): Proxy<R3>; (...args: A4): Proxy<R4>; }:
    T extends
        { (...args: infer A1): infer R1; (...args: infer A2): infer R2; (...args: infer A3): infer R3; } ? 
        { (...args: A1): Proxy<R1>; (...args: A2): Proxy<R2>; (...args: A3): Proxy<R3>; }:
    T extends
        { (...args: infer A1): infer R1; (...args: infer A2): infer R2; } ? 
        { (...args: A1): Proxy<R1>; (...args: A2): Proxy<R2>; }:
    T extends
        { (...args: infer A1): infer R1; } ? 
        { (...args: A1): Proxy<R1>; }:
    Proxy<T>

type Wrap<O> = O extends (...a: infer P) => infer R
    ? (...args: P) => Proxy<R>
    : Proxy<O>

class A {
    m(a: boolean): any;
    m(a: number): any;
    m(a: string): any;
    m(...a: any[]) { }
}

let p = {} as Proxy<A>;
p.m('1') // > returns Proxy<any>
p.m(1)   // > returns Proxy<any>
p.m

Playground Link

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

Convert individual packages within the node_modules directory to ES5 syntax

I am currently working on an Angular 12 project that needs to be compatible with Internet Explorer. Some of the dependencies in my node_modules folder are non es5. As far as I know, tsc does not affect node_modules and starts evaluating from the main opti ...

Trouble with storing data in Angular Reactive Form

During my work on a project involving reactive forms, I encountered an issue with form submission. I had implemented a 'Submit' button that should submit the form upon clicking. Additionally, there was an anchor tag that, when clicked, added new ...

Creating a custom Map type in TypeScript

I am exploring the concept of defining a Map type in Typescript using generics. Essentially, I want to create something similar to: EntityMap<U, V>, where U can only be either a string or a number This is what I have managed to come up with so far: ...

Transferring information using TypeScript

My issue arises when transferring data from HTML in the following format Karbohidrat :{{karbohidrat}} <button ion-button (click)="cekHalamanMakanan('karbohidrat')">View carbohydrate foods</button> <br> Then, I retrieve the kar ...

Obtain the appropriate selection in the dropdown based on the model in Angular

I am working on a dropdown menu that contains numbers ranging from 1 to 10. Below is the HTML code for it: <div class="form-group"> <label>{{l("RoomNumber")}}</label> <p-dropdown [disab ...

The conversion of an array to Ljava/lang/Object is not possible

I'm currently working on a project using NativeScript app with TypeScript where I am trying to pass an array of android.net.Uri to a function. However, when attempting to do so, I encounter an error mentioning that the 'create' property does ...

What is the proper way to create an array of dynamically nested objects in TypeScript?

Consider the structure of an array : [ branch_a: { holidays: [] }, branch_b: { holidays: [] }, ] In this structure, branch_a, branch_b, and ... represent dynamic keys. How can this be properly declared in typescript? Here is an attempt: ty ...

Can the lib property in tsconfig.json override the target property?

Just starting out with Typescript, I have a query regarding the lib and target properties. Below is my tsconfig.json file: { "compilerOptions": { "target": "es5", "outDir": "./dist", "rootDir": "./src", "noEmitOnError": true, } } //index.ts consol ...

Unable to locate dependencies while testing the react package locally

Recently, I came across this npm package designed for React using Typescript. To debug it locally, I initiated npm link in a new React project but encountered an error: https://i.sstatic.net/nObH6.png I suspect it may not be reading the packages correct ...

Using ngFor results in duplicate instances of ng-template

I'm facing a challenge with the ngFor directive and I'm struggling to find a solution: <ng-container *ngIf="user.images.length > 0"> <div *ngFor="let image of images"> <img *ngIf="i ...

What is the best way to transmit two distinct sets of data from a child component to the v-model of a parent component?

Currently, I am working on a project using vuejs 2 and typescript. In this project, I need to pass two different sets of data - data and attachments - within the parent component. I am utilizing vue-property-decorator for this purpose. However, I am facing ...

The listener for @ok is not being activated when using jest-test-utils with b-modal in vue-bootstrap

I have implemented the vue-bootstrap b-modal feature with the @ok="save" hook Here is a snippet of mycomponent.vue: <template> <div> <b-button @click="add">open modal</b-button> <b-modal static lazy id="modal-detail" ...

Unable to retrieve a random element from an array in Angular 10

I'm attempting to shuffle items (cards that have English words) from a flashCards array, so that each card can appear randomly when the user reloads the page. I tried using the Math.floor(Math.random()) function, but it's not working. How can I r ...

Utilizing the array.map method to access the key of an object within an array of arrays in TypeScript

Can I utilize array.map in TypeScript to apply a function with the parameter being the key of an object within an array? I have an array containing objects which have keys 'min' and 'max'. I am looking to use something like someArrayFun ...

What are the steps to display an Angular Component on an HTML template?

Currently, I am in the process of creating a web application utilizing Angular 6. I have encountered a minor roadblock with a simple code snippet present in one of my services: method() { document.body.insertAdjacentHTML('beforeend', '&l ...

Optimizing and scaling Firebase for improved performance

Is it possible to efficiently retrieve schedules from a database with thousands, or even millions, of records in the future? I am planning on storing schedules from healthcare professionals in a collection, but I am unsure if it is better to store them wi ...

Can you dynamically create screens within the React Navigation Tab Navigator using code?

My goal is to dynamically generate tabs in React-Navigation based on the values retrieved from an array, and pass the data from each array value to the corresponding screen. For instance, if there are 2 accounts, I expect 2 tabs with unique screens for eac ...

What causes the object type to shift away from 'subscribe'?

Currently, I am facing an issue with retrieving a Coupon object from the server using a REST API in combination with Angular. The problem arises when I attempt to access the 'subscribe' method - within the 'subscribe', the object is of ...

Create an entity with a field that holds a value type based on the value of another key field

Essentially, I am looking to create a customized "Pair" data type For example: type Pair<T extends Record<string, string | number>, K extends keyof T> = { field: K, value: T[K] } So, if we have: type Rabbit = { name: string, a ...

Troubleshooting: Issues with importing Less files in TypeScript using Webpack and css-loader

I am currently working on a test project using TypeScript and Webpack. I have an index.ts file and a base.less (or base.css) file imported in the index.ts, but I am experiencing errors with the css-loader. Interestingly, everything works fine when the LESS ...