0
votes

I know that function overloading is not supported in typescript and javascript.

And I'm studying a detour method to work like this function overloading.

My case is as follows.

first:

The last ans first argument is fixed.

public A(arg1:number, arg2:number, argLast:any){

}

public A(arg1:number, arg2:number, arg3:Array<any>, argLast:any){

}

Second :

enter image description here

There is an indicator for whether the function is overloaded.

Of course, as in the above example, it is possible, but I have to create it through a new interface, so it does not fit in my case.

I have tried various function overloading methods.

I also implemented it through john resig blog.

(https://johnresig.com/blog/javascript-method-overloading/)

The code below is an example I made through the above blog.

function addMethod (object, name, fn) {
  var old = object[ name ]
  object[ name ] = function () {
    if (fn.length === arguments.length) {
      return fn.apply(this, arguments)
    } else if (typeof old === 'function') {
      return old.apply(this, arguments)
    }
  }
}

export class Users{
  find(...arg: any[]){
    Users.prototype['findOVF'](arg)
  }
}
addMethod(Users.prototype, 'findOVF', function () {
  console.log('ARG 0')
})
addMethod(Users.prototype, 'findOVF', function (name:string, age:number) {
  console.log('ARG 2')
})
addMethod(Users.prototype, 'findOVF', function (first_name:string, last_name:string,age:number) {
  console.log('ARG 3')
})

var users = new Users()
users.find() 
users.find('John',19) 
users.find('John', 'Resig', 19) 

When I use this method, I call the function with the parameter (... arg).

I've tried to implement all the methods of the blog below.

(https://blog.mariusschulz.com/2016/08/18/function-overloads-in-typescript)

public A(arg1:number, arg2:number, argLast:any){

}

public A(arg1:number, arg2:number, arg3:Array<any>|null, argLast:any){

}

If I implement function overloading this way, I get an error every time I call the A function. It's not really function overloading.

But I have not found a way to implement these two elements at the same time.

I want to know if this is impossible with the typescript grammar or if there is any other possibility.

Thanks for seeing my awkward English.

1
It's not really clear to me what does not work. COuld you post a complete example with the code you would like to work but does not work or does not work as expected ? - Titian Cernicova-Dragomir
ok. i`ll post right now. However, the writing will be very long. I hope you understand. - naiad
I tried to describe the method I tried. I have a question too ... I'm sorry. - naiad

1 Answers

2
votes

You can create overloads in typescript but all the overloads have a single implementation and it's up to you to differentiate between them. The simplest solution, if your methods are all differentiated just by number of parameters is this:

class User { }
export class Users {
    find(): User;
    find(name: string, age: number): User;
    find(first_name: string, last_name: string, age: number): User
    find(...args: [any?, any?, any?]): User {
        if (args.length == 0) {
            return null as any;
        } else if (args.length == 1) {
            let [name, age, _]: [string?, number?, any?] = args;
            console.log(`${name}, ${age}`);
            return null as any;
        } else if (args.length == 2){
            let [first_name, last_name, age]: [string?, string?, number?] = args;
            console.log(`${first_name}, ${last_name}, ${age}`);
            return null as any;
        } else {
            throw "Not implemented";
        }
    }
}

Playground link

The above solution preserves call site type safety, your solution does not since the argument to find is any[] in your example.

You could use a more automated approach by defining a function that will create an overloaded function form an array of functions but wather this extra complexity is worth it you be the judge.

type UnionToIntersection<U> =
    (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

function overloads<TThis>(){
    return function<T extends Array<(this: TThis, ...args: any[]) => any>>(...fns: T): UnionToIntersection<T[number]> {
        return function (this: TThis, ...args: any[]) {
            for (var fn of fns) {
                if (fn.length === args.length) {
                    return fn.apply(this, args);
                }
            }
            throw "Not implemented";
        } as any
    }
}

class User { }
export class Users {
    member: string = "M"
    find = overloads<Users>()(
        function () {
            console.log('ARG 0');
            this.member // this is Users 
        },
        function (name: string, age: number) {
            console.log('ARG 2')
        },
        function (first_name: string, last_name: string, age: number) {
            console.log('ARG 3')
        }
    );
}

var users = new Users()
users.find('John', 19)
users.find('John', 'Resig', 19) 

Playground link

Or a version that assigns the function to the prototype not the instance:

export class Users {
    member: string = "M"
}

let find = overloads<Users>()(
    function () {
        console.log('ARG 0');
        this.member // this is Users 
    },
    function (name: string, age: number) {
        console.log('ARG 2')
    },
    function (first_name: string, last_name: string, age: number) {
        console.log('ARG 3')
    }
);
export interface Users {
    find: typeof find;
}
Users.prototype.find = find;

Playground link