interfaces and implementing them

Topics: General
Oct 13, 2012 at 8:06 AM

I expected the following code to compile fine. When I put the two method signatures in the class declaration it all goes fine (the multiple signatures, one implementation rule). But when I put them in a separate interface, the compiler complains about incompatible types.

interface ITest {
    foo(a:number,b:number) : number;
    foo(a:string,b:string) : string;
}

class Test implements ITest {
     foo(a:any,b:any):any { 
         return null;
      }
}

Can anyone shed some light?

--Peter

Developer
Oct 13, 2012 at 6:14 PM

Here's how to write it:

interface ITest {
    foo(a: number, b: number): number;
    foo(a: string, b: string): string;
}

class Test implements ITest {
    foo(a: number, b: number): number;
    foo(a: string, b: string): string;
    foo(a: any, b: any): any {
        return null;
    }
}

When you implement an interface the compiler checks that your class is a subtype of that interface. That wasn't the case in your original example, specifically because { (a: any, b: any): any; } is not a subtype of { (a: number; b: number): number; (a: string; b: string): string; }.

Anders

Oct 13, 2012 at 7:39 PM

Thanks for the reply.

Still a small question remaining. The compiler knows that  (a: any, b: any): any; } is the implementation of { (a: number; b: number): number; (a: string; b: string): string; }. So why would I have to repeat the two methods in my class definition?

When I just use the following code, the compiler also understands that  (a: any, b: any): any; } implements the two other methods.

class Test  {
    foo(a: number, b: number): number;
    foo(a: string, b: string): string;
    foo(a: any, b: any): any {
        return null;
    }
}

So why when I factor out those two "abstract" methods into an Interface, I have to repeat them again in the class if the compiler clearly already understands this relationship? I would have thought if my class "implements" the interface it would be enough (it doesn't have to be a formal subclass).

 

BTW, done some re-factoring of existing JS code into TypeScript, and it is really nice how gradually you can add typing-info to your code to assist you in findings possible issues. Didn't expect such smooth ride.

 

--Peter

Developer
Oct 13, 2012 at 10:05 PM
Edited Oct 14, 2012 at 12:18 AM

When you list an interface in the implements clause of a class you're instructing the compiler to verify that your class is a proper implementation of the interface. In type system terms, a proper implementation is one that is a subtype of the interface, so that's what the compiler checks. Now { (a: any, b: any): any; } is not a subtype of { (a: number, b: number): number; (a: string, b: string): string; } because an 'any' function result could be anything and not just a number or string as the contract requires. This means someone might call your interface implementation and get a result they're not prepared to deal with. So, you're required to implement an overloaded function where you explicitly state that you are aware of the contract. Being a static type system we can't actually check that you're doing what you're supposed to in the implementation, but at least you have been warned.

BTW, in your original example, a Test is actually assignable to ITest. This is because in assignments we basically view type 'any' as a wildcard. However, the 'implements' clause is a stricter check that verifies a proper subtype relationship.

Hope this helps.

Anders

Oct 14, 2012 at 12:37 PM

Thanks for the detailed explanation, it does indeed help.

At first I thought that "assignable" should be good enough for determining whether a class implements an interface. But after some more careful considerations I can see the benefits of the more stricter "sub-type" check.

Now when I add a new method to the interface, lets say:

foo(a:bool,b:bool): bool;

the compiler will now complain about my class not implementing all of the interface. In the way I had envisioned it to work, the compiler would be happy and I would have not have known that I might need to update my class due to this interface change.

-- Peter