I'm trying to take advantage of the TypeScript typecheck, but I'm stuck with the following code:
abstract class Mammal {
abstract breed(other: Mammal);
}
class Dog extends Mammal {
breed(other: Dog) {}
}
class Cat extends Mammal {
breed(other: Cat) {}
}
const toby = new Dog();
const lucy = new Dog();
const luna = new Cat();
toby.breed(lucy); // OK
toby.breed(luna); // Works, but it shouldn't since luna is a Cat!
It seems like TypeScript is performing some kind of duck typing here and is considering Dog == Cat
. How can I get the typechecker to reject this code?
Sidenote: This is how I would do this in Rust:
trait Mammal { fn breed(&self, other: &Self); } struct Cat {} impl Mammal for Cat { fn breed(&self, _other: &Self) {} } struct Dog {} impl Mammal for Dog { fn breed(&self, _other: &Self) {} } fn main() { let toby = Dog{}; let lucy = Dog{}; let luna = Cat{}; toby.breed(&lucy); toby.breed(&luna); // expected struct `Dog`, found struct `Cat` }
TypeScript is structurally typed (thanks to @jcalz).
The idea behind structural typing is that two types are compatible if their members are compatible.
Not so elegant but working: You can add a dummy property to make some differences between the classes (Playground):
abstract class Mammal {
abstract breed(other: this): void; // Bonus: Here you can make use of Polymorphic this types
}
class Dog extends Mammal {
private dummy1 = undefined;
breed(other: Dog) {}
}
class Cat extends Mammal {
private dummy2 = undefined;
breed(other: Cat) {}
}
const toby = new Dog();
const lucy = new Dog();
const luna = new Cat();
toby.breed(lucy); // OK
toby.breed(luna); // Error
Bonus: You can make use of Polymorphic this types (thanks to @jcalz) in your base class.
abstract breed(other: this): void;