Interface used as an object type……

Topics: Language Specification
Mar 11, 2014 at 2:33 AM
My codes:

interface DelegateFun {
(a: number): number;
(a: number, b: number): number;
}

I can say:

var a: DelegateFun = (a) =>
{
return a;
};

But I cannot say:
var a:DelegateFun=(a,b)=>{……};
It seems that if you define more than one object type in an interface, only the object type with less parameters will be the default one, others will be ignored, right?

Another question is:__Can such interfaces be implemented directly by a class?If not, where's the usage of such interfaces with more than one object types?__
Developer
Mar 11, 2014 at 8:33 PM
Your interface is not defining more than one object type, it's defining more than one call signature for the type. Now an instance can be called like:
var d: DelegateFun;
var r = d(1);
var r2 = d(1,2);
so from that you can see why allowing this assignment:
d = (a,b) => { ... } // what if the body of this lambda dots off of b?
isn't safe. You've provided an implementation that has 2 required parameters when the interface explicitly says it can be called with only a single parameter.

Interfaces with call signatures cannot be implemented by classes. The usage is exactly as you attempted to do above. You can still use it to provide strong typing over function expressions, lambdas, or object literal types.
var d: DelegateFun;
var a = (a) => a + 1;
a('hello'); // no error
var d: DelegateFun = (a) => a + 1;
d('hello'); // error

var x: { d: DelegateFun; } = { d: (a: string) => a }; // error
var y: { d: DelegateFun; } = { d: (a: number) => a }; // no error
Marked as answer by ProgramVolunteer on 3/12/2014 at 6:27 PM
Mar 12, 2014 at 3:05 AM
Edited Mar 12, 2014 at 3:57 AM
danquirk wrote:
Your interface is not defining more than one object type, it's defining more than one call signature for the type. Now an instance can be called like:
var d: DelegateFun;
var r = d(1);
var r2 = d(1,2);
Many thanks!
But there's a strange thing——I can write "var r2=d(1,2)" but I never define a function for d(1,2) (two parameters)at all……And the result will be the same d(1)——Why? I'm using 1.0RC version of TS and VS is 2012. I can see that the converted js file is a(1,2)……
Mar 12, 2014 at 4:03 AM
danquirk wrote:
so from that you can see why allowing this assignment:
d = (a,b) => { ... } // what if the body of this lambda dots off of b?
isn't safe. You've provided an implementation that has 2 required parameters when the interface explicitly says it can be called with only a single parameter.
Nice explainations! But this causes another syntax problem——First I wanna confirm this:

If you use
interface ICaller
{
   (a:number):number;
}
This, in fact, equals to:
var b:{(a:number):number)}=>{……};
Now we cannot define more than one object type to substitute for "{(a:number):number)}"——because obviously speaking, it cannot return you more than one object type. Right?

If so, why the interface allows us to define more than one object types? I mean this:
interface ICaller
{
   (a:number):number;
   (a:number,b:number):number;   //This should be wrong, because a variable applied by ICaller cannot return you the object type at all!
}
Another example is:
interface ICaller
{
   (a:number):number;
   (b:string):string;
}
How can you use that?
Mar 12, 2014 at 4:06 AM
Edited Mar 12, 2014 at 5:37 AM
var d: DelegateFun = (a) => a + 1;
d('hello'); // error
No, this is right. Because DelegateFun is using only one object type, this will be compiled as this below:
var d:{(a:number):number}=(a)=>{return a+1;};
I can pass the compile and run well in an HTML page when referring the ts file.

PS: Ignore you pass 'hello' as a string parameter causing the problem;)
Developer
Mar 13, 2014 at 12:41 AM
ProgramVolunteer wrote:
But there's a strange thing——I can write "var r2=d(1,2)" but I never define a function for d(1,2) (two parameters)at all……And the result will be the same d(1)——Why? I'm using 1.0RC version of TS and VS is 2012. I can see that the converted js file is a(1,2)……
This is behavior inherited from JavaScript. Calling a function with more arguments than it requires is ok (as long as the types of the listed parameters do match).

ProgramVolunteer wrote:
Now we cannot define more than one object type to substitute for "{(a:number):number)}"——because obviously speaking, it cannot return you more than one object type. Right?

If so, why the interface allows us to define more than one object types? I mean this:
interface ICaller
{
   (a:number):number;
   (a:number,b:number):number;   //This should be wrong, because a variable applied by ICaller cannot return you the object type at all!
}
Another example is:
interface ICaller
{
   (a:number):number;
   (b:string):string;
}
How can you use that?
These are equivalent:
interface ICaller {
    (a: number): number;
}

var i: ICaller;
var x: { (a: number): number };
x = i;
i = x;
Your last two interfaces are names for types which have multiple call signatures. An implementation for those types can handle these overloads however they choose. For example:
interface ICaller {
    (a: number): number;
    (b: string): string;
}

var i: ICaller = (x: any) => {
    if (typeof x === 'number') {
        return <any>1;
    } else if (typeof x === 'string') {
        return 'hello';
    }
}
var r1 = i(1); // number
var r2 = i(''); // string;
var r3 = i(true); // error, i only accepts a number or a string
Marked as answer by ProgramVolunteer on 3/12/2014 at 6:27 PM
Mar 13, 2014 at 2:27 AM
So must I convert number, boolean type to "any", other types are all "reference types" so can be implicitly converted to "any"?
Developer
Mar 24, 2014 at 7:59 PM
No. The reason for the cast to 'any' in the last example is because for a function with multiple return statements and no return type annotation the compiler must infer a return type by determining a best common type. There is no useful best common type between string and number, but there is between string and any. Alternatively, when possible you could just use an explicit return type annotation. Ex:
// error
function foo() {
    if (true) {
        return 1;
    } else {
        return '';
    }
}

// no error
function foo2() {
    if (true) {
        return <any>1;
    } else {
        return '';
    }
}

// no error
function foo3(): any {
    if (true) {
        return 1;
    } else {
        return '';
    }
}