class Base {
property: string;
}
class Dog extends Base {
name: string;
eat() {
console.log("[eat] Dog eats");
}
}
class Cat extends Base {
// only if `name1` is uncommented typescript will start throwing an error
// name1: string
eat() {
console.log("[eat] Cat eats");
}
}
function test(): Cat {
return new Dog();
}
Expected behavior:
Type Dog is not assignable to type Cat
Actual behavior:
No errors, error only appears if another property is added to the other class.
You should read the FAQs. Dog is assignable to Cat because they are structurally compatible and Cat is a super type to the subtype Dog. TypeScript is not a nominally typed language, so it doesn't care "who" constructed the object or their inheritance chain.
I agree that an interface should match unless adding a 'brand'. However a class is different, this can create a lot of issues if someone does not realise about the problem and it is not viable to add a 'brand' to every class to be safe.
However a class is different, this can create a lot of issues if someone does not realise about the problem and it is not viable to add a 'brand' to every class to be safe.
That is why I am educating you on the way the TypeScript type system works, so you realise the problem. From a strict type perspective the operation you are doing is sound. There are very valid and strong reasons why TypeScript is a structurally typed language.
This is one of many duplicates of #202.
Thanks for that :) it would be nice if it would be handled in the future, mostly if you are provided with an API and in one case it is running another class logic which is unexpected, this defeats one of the purposes of using TypeScript which is to spot errors while you are writing code. I'll close this issue since it is a duplicate :)
Most helpful comment
I agree that an interface should match unless adding a 'brand'. However a class is different, this can create a lot of issues if someone does not realise about the problem and it is not viable to add a 'brand' to every class to be safe.