Aug 24, 2015

TypeScript is duck typing.

When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.

Typescript a superset of typescript, since Javascript is a duck typing language, typescript is also a duck typing language. In strong type languages the signature of an object is important and type is the signature of an object, they care about whether the signature are compatible. Typescript does not care about signature and type, cares about whether objects are replaceable because of what they can actually do.

Typescript interface is not a signature but semantics

Type compatibility in TypeScript is based on structural subtyping, it relates types based solely on their members. In the following example, variable foo and bar are declare of different interface type. The literal object can be assigned the variable foo, because typescript compiler can infer the ability of the literal object(this is called type inference), and typescript compiler think that literal object can do what interface is expected to do. And foo can be assigned to bar, because foo can do what interface Bar is expected to do. The name of interface (Foo and Bar) does not matter, what is matter is semantics of the interface.

//--interface is semantics
interface Foo {
  quack();
}

interface Bar {
  quack();
}

var foo : Foo;
var bar: Bar;

foo = {
   quack: function () {}
};

bar = foo;

Typescript class is just an interface with implementation

One of the reason that people use Typescript is that it support class. However, typescript class is different from C# and Java class. The following are similar to Java class. You can create an instance of a class by using the keyword "new", you can inherit a class by using keyword "extends".

//--class as implementation
class Duck {
    constructor(public name: string) {
    }
    quack() {
        console.log('Duck quack ' + this.name);
    }
}

class Swan extends Duck {
    fly() {
        console.log('Swan fly ' + this.name);
    }
}

var duck = new Duck('duck1');
duck.quack();
//
var swan = new Swan('swan1');
swan.quack();
swan.fly();

But a less well-known fact is that typescript class can be used as an interface, like the following code

//---class as interface----
var objectOfAnounymousType = {
    name: 'Daisy',
    quack: function () {
        console.log('quack quack ' + this.name);
    }
};
var daisy: Duck = objectOfAnounymousType;
daisy.quack();

class Person implements Duck {
    name = "Person"
    quack() {
        console.log('Person quack ' + this.name);
    }
}

var john = new Person();
john.quack();

daisy = john;
daisy.quack();

When typescript compiler see the a literal object, it can infer an anonymous type from the object. When the object is assigned to a variable of Duck class, typescript compiler find that the anonymous type is compatible to Duck class(interface) because the anonymous type has all the members that Duck class has. The class Person does not extend (inherit) Duck class, instead it treat Duck class as an interface, and implements it.

The difference between interface and class is that interface does not has implementation but class has implementation. Because of this, class can implement interface, but interface can not implement interface.

interface IDuck {
    name: string,
    quack: () => void
}

interface ISwan extends IDuck {
    fly(): void
    
}

class Duckling implements IDuck {
   constructor(public name: string) {
    }
    quack() {
        console.log('Duckling quack ' + this.name);
    }
}

Thanks for taking the time to read this. Feedback, critiques and suggestions are welcomed.