4

I was having a play around with TypeScript and interfaces. I have the following piece of code

interface Control {
    name: string;
    onSelect():string;
}

class Button {
    name:string="button";
    onSelect = function() {
        return "hello";
    }
}

var a:Button = new Button();
var b:Control = {"name": "arbitrary", "onSelect": () => { return "ahoy!"; }};

var trigger = function(c:Button) {
    console.log(c.name, "says", c.onSelect());
}

trigger(a);
trigger(b);

Which compiles and runs without complaining. Can anyone please explain why my trigger function accepts b even though it expects to get a type Button and b is of type Control.

Even if Button were to explicitly implement Control, I'm asking for a Button not a Control. For all intended purposes, Button may contain additional members.

Is TypeScript inferring the implementation just because they are structurally the same? Are you allowed to pass an interface where an implementing class is expected? (shouldn't it be the other way around?)

3
  • 1
    If Button has a private member, only it (or classes that extend from it) will be considered compatible types. You might consider adding a 'dummy' private member if this is the desired behavior. Commented Dec 11, 2014 at 19:18
  • I see. I was thinking of adding a dummy member. In your opinion, is strict type enforcing something unnecessary? Does it go against the principles of TS? Commented Dec 11, 2014 at 19:24
  • I'd say it's rarely productive, but there are always exceptions. It's somewhat of a design smell if trigger actually depends on aspects of Button other than the members it uses, after all. Commented Dec 11, 2014 at 22:25

2 Answers 2

8

As described in this TypeScript doc:

One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural typing”.

In "duck typing":

If it looks like a duck and swims like a duck, then in it's likely a duck.

In other words, TypeScript examines the shape (methods and properties) of a supplied object or type. If the supplied object contains all the properties and methods described by an interface, then the object can, in all likelihood, be treated as an object that is compatible with the described interface.

In your example, your interface and class look exactly the same, so TypeScript treats your object as compatible with the described interface.

If you examine the generated JavaScript, note that there is no mention of your interface at all - this is because TypeScript Interfaces are essentially developer-supplied type metadata that help TypeScript validate type compatibility.

Sign up to request clarification or add additional context in comments.

3 Comments

Is there any way to enfore strict type checking? This makes it easier for me to overlook subtle nuances that might introduce problems down the line.
@Andrei - Type safety is not "The JavaScript Way™" Many JS programmers like the fact that they can fly fast and loose with types. TS adds some type-safety, but it's still looser than C/C++/C#/Java/etc.
@Andrie: Further, as John mentions, if you make your object or interfaces look different, then TS will warn you.
2

Both your interface and class have the same implementation, so while its expecting a Button you are passing in a compatible object (has name that returns string and onSelect function that returns a string)

If you add another property to Button such as greeting: string it will error on trigger(b) saying that its not valid.

interface Control {
    name: string;
    onSelect:() => string; // this is another way to write function that returns a string
}

class Button implements Control {
    name:string="button";
    onSelect = function() {
        return "hello";
    }
    greeting: string; // adding this will cause trigger(b) to fail
}

2 Comments

Do you know if there is any way to enable strict type checking? Would it be something you consider unnecessary?
@AndreiNemes I don't think its necessary. Once your class has any other kind of property or isn't exactly match the interface it wouldn't allow it.

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.