Issue With Function Overloading

Topics: General, Language Specification
Jan 22, 2014 at 7:28 PM
Edited Jan 22, 2014 at 7:30 PM
I'm not sure if this is a bug or expected behavior, but it appears that the way TypeScript is resolving overloads is almost making the feature useless. For example:
class Parent
{
    ParentMember = 1;
}

class Child extends Parent
{
    ChildMember = 1;
}

function F(a: Parent): Parent
function F(a: Child): Child
{
    return a;
}

var p = new Parent();
var c = new Child();

F(p);
F(c).ChildMember; // Error
I'm basically trying to make a specialization so that if you pass in a Child, an Child is returned, but if you pass in a Parent, a Parent is returned. I can't seem to make this work.

As it stands, F(c).ChildMember isn't compiling, presumably because it's resolving to the first overload and then quitting out, ignoring the fact that there is a better overload further down.

If I reverse the order of the overloads, it doesn't seem to work at all -- an error both on the first F() call as well as an error that "Child" is not a subclass of "Parent".

If I add an "any" overload at the bottom, the type passes through, but I've now exposed a type safety hole because an "any" can be passed into the method.

My personal opinion is that I feel that TypeScript's overload resolution needs rethinking. I've been writing TypeScript all day for some time now, and so far, each time I've attempted to define an overload, I've had to back out due to a limitation of the language. Hopefully this is just my own misunderstanding.
Developer
Jan 22, 2014 at 8:39 PM
The implementation signature does not count as an overload signature. Since at runtime there's only actually a single function implementation that generally does the necessary run time type tests you have to explicitly write out each overload signature you want to make available for callers. For example:
function F(a: Child): Child
function F(a: Parent): Parent
function F(a: any): Child {
    if (a.ChildMember) {
        ...
    }
    else if (a.ParentMember) {
        ...
    }
    else { throw 'oops' }
}
does what you were trying to achieve.