"Implicit" interface

Topics: Language Specification
Nov 19, 2013 at 8:30 PM
From Example 5, I found that there is not explicit relationship between Person and Student, as I would expect from a strong-typed programming language:
class Student implements Person { ... etc ... }
The provided example works as is (without the explicit interface co-relation), meaning that the interface is inferred somehow at the moment the Student object is passed to the greeter() argument.
I think this goes against the whole purpose of having a strong typed programming language. it means that the type is evaluated based on member-set name matching, not on explicit type declarations.
I can figure out several scenarios where this is inconvenient for maintainability (my main reason for adopting TypeScript). Two classes can share the same signature at some moment and then "accepted" by the greeter() method, but later the field names can be changed.
Exaggerating a bit: I can declare the Pet class to have firstname and lastname, but later decide to change to nickname. That implies that an Pet instance can be passed to greeter() in my initial implementation although conceptually pets cannot be persons.
Besides the philosophical discussion, this kind of inference wouldn't be allowed in C# or other similar language.
Coordinator
Nov 19, 2013 at 11:20 PM
One way to think about the interfaces is as a way to define a specific API. If you implement that API, then you pass the test.

You can also enforce something stricter by using how private members work in TypeScript. For example:
class Person {
    private id;   // this private is unique, so only subclasses will 
                  // pass checks that require "Person"
}

class Student extends Person { 
    name: string;
}

function checkPerson(p: Person) { }

checkPerson({name: "bob"}); // errors because object literal isn't a subtype of Person
Developer
Nov 20, 2013 at 11:15 PM
Many strongly typed programming languages use a structural type system (which is what TypeScript uses) and not a nominal one (like C#), for example some of the ML family of functional languages (which have served as inspiration for a number of recent C# features). There is nothing inherently weaker about it. The interface is not inferred at the callsite, the callsite simply checks whether you have the required set of properties it claims to need. As Jonathan notes, declaring a class heritage specification with an interface serves as an explicit statement of a contract about your type. It does not preclude other types from also fulfilling that contract at some other point.

While you are correct that C# would not allow certain patterns like this, the converse is that there are a number of useful patterns which C# doesn't easily allow because things which are functionally equivalent do not match only because their name and heritage are different. The structural typing is an important part of modeling how JavaScript is used today.

If I'm asking for the string version of your name then why do I care if you're a person or a dog? You have a name, just tell me it.
Coordinator
Nov 21, 2013 at 12:37 AM
Since everyone's chiming in :)

Even nominally-typed type systems can fall prey to the problem that semantics are not necessarily implied by the type system. Witness the ICloneable interface in .NET - some types implemented Clone as a deep copy, some as a shallow copy, and some as something else entirely. If someone gave you an ICloneable, you'd have no idea what the exact result of calling Clone was. Today the guidance is "Don't use ICloneable". There's no magic bullet for this sort of problem, and structural type systems seem to better model common JavaScript programming practices than nominal system.