TypeScript Tutorial
TypeScript Polymorphism
A base type can hold a derived object. Call methods through the base and the derived version runs.
One call, the right version
You have an Animal and two kinds of animal. You want speak() to mean bark for a dog and meow for a cat, even when the code that calls speak only knows it has an Animal.
That is polymorphism: the same name, different behavior chosen from the real type of the object. In TypeScript you store a derived object in a base-typed variable and mark the replacement with override. Methods are already virtual. You do not write a virtual keyword.
override speak()
Write the method on the base class. In the derived class, write the same signature and addoverride. override asks the compiler to check that you really replaced a base method. A typo in the name then fails at compile time instead of silently adding a new method.
Example
class Animal {
public speak(): void {
console.log("...");
}
}
class Dog extends Animal {
public override speak(): void {
console.log("woof");
}
}
class Cat extends Animal {
public override speak(): void {
console.log("meow");
}
}
const d: Dog = new Dog();
const c: Cat = new Cat();
d.speak();
c.speak();Direct calls on d and c already pick the derived version. Polymorphism matters most when the static type is the base.
A base variable holds a derived object
Create a Dog and store it in an Animal variable. Call speak through that variable. The program runs Dog.speak, not the base body.
Example
class Animal {
public speak(): void {
console.log("...");
}
}
class Dog extends Animal {
public override speak(): void {
console.log("woof");
}
}
const d: Dog = new Dog();
const p: Animal = d;
p.speak();p is typed as Animal. The object is still a Dog. You can also writeconst p: Animal = new Dog(); in one step. The variable’s type is the base. The value’s type is the derived class.
Through p you can call methods that exist on Animal. You cannot callbark on p unless bark is also declared on the base. The compiler looks at the variable’s type.
Several types, one loop
Put different animals in an array typed as Animal[]. The loop only knows Animal. Each object still speaks as itself.
Example
class Animal {
public speak(): void {
console.log("...");
}
}
class Dog extends Animal {
public override speak(): void {
console.log("woof");
}
}
class Cat extends Animal {
public override speak(): void {
console.log("meow");
}
}
const zoo: Animal[] = [new Dog(), new Cat()];
for (const a of zoo) {
a.speak();
}That is the payoff. Drawing code, UI widgets, and game entities all use this pattern: store base-typed values, call one method, get the derived work.
abstract when the base has no real body
Sometimes the base should not be constructed at all. Mark the class abstract and mark the methodabstract with no body. Derived classes must implement it. new Shape() then fails to compile; new Square(4) is the object you actually create.
Example
abstract class Shape {
public abstract area(): number;
}
class Square extends Shape {
private side: number;
constructor(side: number) {
super();
this.side = side;
}
public override area(): number {
return this.side * this.side;
}
}
class Circle extends Shape {
private radius: number;
constructor(radius: number) {
super();
this.radius = radius;
}
public override area(): number {
return Math.PI * this.radius * this.radius;
}
}
const shapes: Shape[] = [new Square(4), new Circle(1)];
for (const s of shapes) {
console.log(s.area());
}Each value in shapes is a Shape as far as the loop knows. The square prints 16. The circle prints a number near 3.14. A later chapter stays on abstract classes. Here they are the strict form of “call through the base.”
Rules that keep this small
- Store derived objects in a base-typed variable or array when the caller should not care which subclass it has.
- Write
overrideon the replacement. tsc then checks the name and the parameter list. - The signatures must match: return type, name, and parameters.
- Use
abstracton the base when there is no sensible default body.
Compile these at /typescript/try. Next: splitting a program across files withexport and import.