I came up with one way to get stronger typing. I do not like it much. One adds a special field or method to each type that will make it incompatible with others that would be confused as ducks.
The following does not allow substitution of a Parrot for a Duck, because the Duck class has an additional method (so Parrot fails duck typing). Sparrows and Parrots are apparently substitutable in duck typing because there's nothing a parrot can do that a sparrow cannot, and vice versa. Of course, a Duck can substitute for a Parrot, because if is sounds like a parrot, it is a parrot.
Test with www.typescriptlang.org/Playground/ :
class Sparrow {
sound = "cheep";
}
class Parrot {
sound = "squawk";
}
class Duck {
sound = "quack";
swim(){
alert("Going for a dip!");
}
}
var parrot: Parrot = new Sparrow(); // substitutes
var sparrow: Sparrow = new Parrot(); // substitutes
var parrotTwo: Parrot = new Duck();
var duck: Duck = new Parrot(); // IDE & compiler error
alert("Parrot says "+parrot.sound+" and sparrow says "+sparrow.sound+", and 2nd parrot says "+parrotTwo.sound);
alert("A duck says "+duck.sound);
More practically, I would do this (which works in my IDE but not in the playground):
interface RawUri extends String {
rawUri;
}
interface EncodedUri extends String {
encodedUri;
}
var e: EncodedUri = new RawUri(); // IDE & compiler error
var r: RawUri = new EncodedUri(); // IDE & compiler error
Distasteful, and an opportunity for another interface to accidentally use the same field name. I suppose one could add a random element to the anti-duck member.