class Dog { woof(){} }
class Cat { meow(){} }
function f(a: Dog|Cat) {
if (a instanceof Dog) {
a.woof()
} else {
a.meow()
}
}
let dogish = {woof: ()=>{}}
f(dogish)
This compiles because dogish is structurally a dog, the type system allows instanceof to narrow the type but "dogish instanceof Dog" is actually false, so at runtime this will crash after trying to call meow on dogish. type userId = string;
type subscriptionId = string;
const uid: userId = 'userA';
const sid: subscriptionId = uid; // compiler is OK with thisHere's how type aliases are usually documented: "Type aliases do not introduce new types. They are equivalent to the corresponding underlying types."
The person above did have a better example of the downside: You usually want something to nominally comply with a specific interface, not structurally. i.e. Just because something happens to have an `encrypt(u8[]): u8[]` method doesn't mean you want to accept it as an argument. (Go interfaces have a similar issue.)
Do this:
interface Dog { typeName: "Dog"; woof():void }
interface Cat { typeName: "Cat"; meow():void }
function isDog(a:Dog|Cat) : a is Dog {
return a.typeName == "Dog"; // Some casting may be required here
}
function f(a: Dog|Cat) {
if (isDog(a)) a.woof();
else a.meow();
}
let dogish : Dog = {typeName:"Dog", woof: ()=>{ console.out("Woof this is Dog")}};
f(dogish);
The neat thing about TypeScript's type system is that it's structural but you can pretty easily implement nominal types on top of it.(And if you only need compile-time checks, you can make typeName nullable; {typeName?:"Dog"} != {typeName?:"Cat"})
type SpecificJsonBlob = {x: number};
function serialize(s: SpecificJsonBlob) { return JSON.stringify(s); }
function addOneToX(s: SpecificJsonBlob) { s.x += 1 }
[...]
let o = { get x() { return 1 } }
serialize(o)
addOneToX(o)
This compiles because the getter makes it structurally conform to the type, but the 'serialize' will surprisingly return just '{}' since JSON.stringify doesn't care about getters, and addOneToX(o) is actually just a runtime crash since it doesn't implement set x. These are runtime issues that would be precluded by the type system in a normal structural typed language.There's obviously other cases of ergonomic benefit to structural typing (including that idiomatic JS duck-typing patterns work as you'd hope), but I think its not unreasonable for someone to feel that it's their biggest problem with typescript (as grandparent OP did).