Reading example from typescript manual:
class Animal {
name:string;
constructor(theName: string) { this.name = theName; }
move(meters: number = 0) {
alert(this.name + " moved " + meters + "m.");
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(meters = 5) {
alert("Slithering...");
super.move(meters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name); }
move(meters = 45) {
alert("Galloping...");
super.move(meters);
}
}
var sam = new Snake("Sammy the Python");
var tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);
The question is about the line var tom: Animal = new Horse("Tommy the Palomino");
:
As I understand tom
is an Animal
with properties of a Horse
. Is that right?
What is the point to make it this way? Not to declare as var tom: Horse = ...
?
Having just one version to give him a chance to degrade/change/evolve to a Snake
or any other Animal
. Am I right?
...or maybe it is just a typo?
In the example above, Animal
is a superclass (also called base class or parent class) of both Horse
and Snake
. Correspondingly, Horse
and Snake
are subclasses (derived classes) of Animal
.
When you declare the subclasses:
class Snake extends Animal
...
class Horse extends Animal
You're telling the compiler that any every Snake
and every Horse
is in fact, an Animal
as well. This makes Animal
the broader category in the "world" of the program. Snake
and Horse
will inherit the properties of Animal
, but they can change them (and/or add a few of their own) to be more specialized.
tom
's declaration tells the compiler that the variable will accept any Animal
. As we saw earlier, a Horse
is an Animal
, so the compiler lets it pass.
Hence, they're illustrating the fact that whenever a member of the superclass is expected in any expression, one member of any of its subclasses is acceptable. This is called covariance.
In the most literal sense, there is no evolving or devolving. The line
tom: Animal = new Horse("Tommy the Palomino");
first causes a new Horse
object to be created. The object is then assigned to the variable tom
, but this assignment does not change the properties of the object. If you ran the example, you'd see that the call horse.move()
actually calls the Horse
version of the method move
, which would report that "Tommy the Palomino moved 45m".
The only discernible side effect of assigning a Horse
to an Animal
is that the variable, being of the most general type, wouldn't know of any specialized properties of Horse
. It only knows what all Animal
s have in common. Let's say Horse
was declared like this:
class Horse extends Animal {
constructor(name: string) { super(name); }
move(meters = 45) {
//...
}
swat_fly() { /* ... */ }
}
You wouldn't be able to call tom.swat_fly()
. If you wanted to, you either need to typecast tom
(like this: (<Horse>tom).swat_fly()
) or declare it as a Horse
instead of as an Animal
. But I reiterate: the object's properties are not changed to the superclass's.
So no, it's not a typo :)