1

While learning Typescript, I stumbled upon this example somewhere on the web. I was surpised to see that this actually compiles:

class Base {
    constructor (public name) { }
    SomeMethod(param:number) {
        console.log(this.name + " " + " called with param:" + param);
    }
}
class Derived1 extends Base {
    constructor(name) { super(name); }
    SomeMethod() {
        console.log("Derived1");
        super.SomeMethod(2);
    }
}
class Derived2 extends Base {
    constructor(name) { super(name); }
    SomeMethod() {
        console.log("Derived2");
        super.SomeMethod(4);
    }
}
var one = new Derived1("one")
var two:Base = new Derived2("two")
one.SomeMethod()
two.SomeMethod(34)

The code is from a blog post I found, and I saw that (probably by mistake) the author changed the signature of "SomeMethod" in the derived classes to something that doesn't take an argument. He then instantiated two objects, one of type "Derived1" and one of "Derived2". In the first case, he used var to automatically make "one" be of type Derived1. In the second case, however, he declared it via var two:Base = ... - so he used a "pointer to base" to access it.

And since the base class has a "SomeMethod" prototype that does, in fact, take an argument, the call two.SomeMethod(34) actually passes from compilation, but at runtime, calls the Derived2 version:

Derived1
one  called with param:2
Derived2
two  called with param:4

Is this not a bug? Shouldn't the typescript compiler catch this case?

1 Answer 1

1

No. It should not be a compiler bug. You should be free to declare any signature you feel suitable for your class as long as it the type is compatible. e.g. void (as in your example) or any as in below example :

class Base {
    constructor (public name) { }
    SomeMethod(param:number) {
        console.log(this.name + " " + " called with param:" + param);
    }
}
class Derived1 extends Base {

    constructor(name) { super(name); }
    SomeMethod(param:any) { // is compatible 
        console.log("Derived1");
        super.SomeMethod(parseInt(param,10));
    }
}

works. However string would give a compiler error:

class Base {
    constructor (public name) { }
    SomeMethod(param:number) {
        console.log(this.name + " " + " called with param:" + param);
    }
}
class Derived1 extends Base {

    constructor(name) { super(name); }
    SomeMethod(param:string) { // not compatible, compiler error  
        console.log("Derived1");
        super.SomeMethod(parseInt(param,10));
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

+1 - good answer. This is a great TypeScript feature, it understands compatibility so you get type safety with being unnecessarily constrained.
I understand your reasoning; however, in this case the end result is that by "two.SomeMethod(34)" the developer expects to call the base class version of SomeMethod (since that's the only one taking an argument) and ends up calling the Derived2 version of the method. IMHO, this should at least trigger a warning.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.