class Person { (1)
firstName = ""; (2)
lastName = "";
constructor(firstName, lastName) { (3)
this.firstName = firstName;
this.lastName = lastName;
}
name() { (4)
return `${this.firstName} ${this.lastName}`;
}
whoAreYou() {
return `Hi i'm ${this.name()}`; (5)
}
}
Class & Interface
Object Orientation in Javascript
Javascript has a prototype-based, object-oriented programming model. It creates objects using other objects as blueprints and to implement inheritance it manipulates what’s called a prototype chain.
We normally call the way object orientation is implemented in C++ or Java as the Classical OO Pattern and the way it’s implemented in Javascript as the Prototype Pattern.
Although the prototype pattern is a valid way to implement object orientation it can be confusing for newer javascript developers or developers used to the classical pattern.
So in ES6 we have an alternative syntax, one that closer matches the classical object orientated pattern as is seen in other languages.
Typescript supports the ES6 class
syntax but also adds some other feature like access modifiers and interfaces, so in this chapter we’ll be writing Typescript rather than pure ES6.
Note
Class
A class
is a blueprint for creating objects with specific functions and properties already attached to it, lets go through a simple example line by line:
-
We define a class using then
class
keyword. -
We describe the properties we want on our class instance.
-
Each class has a special
constructor
function, this is called when we create an instance of a class withnew
-
We describe the functions, also known as methods, that we want on our class instance.
-
this
in a method points to the class instance, the object that is created using this class.
Class Instance
A class is a blueprint for creating an object, we call that created object an instance of a class, or a class instance or just instance for short.
We instantiate a class by using the new
keyword and when that happens javascript calls the constructor
function. We can pass to the constructer arguments which it uses to initialise properties or call other function, like so:
let asim = new Person("Asim","Hussain");
The above creates an instance of the Person class called asim.
The asim instance has the same properties and functions that are described on the Person class:
let asim = new Person("Asim","Hussain");
asim.whoAreYou()
// "Hi i'm Asim Hussain"
Inheritance
A class can inherit from another class. We can create a class blue-print that extends an existing class blue-print by adding other methods or properties.
We do this by using the extends
keyword, like so:
class Student extends Person { (1)
course; (2)
constructor(firstName, lastName, course) {
super(firstName, lastName); (3)
this.course = course;
}
whoAreYou() { (4)
return `${super.whoAreYou()} and i'm studying ${this.course}`; (5)
}
}
-
We use the
extends
keyword to signal that this class inherits all the properties and methods from the parent Person class. -
We can describe additional properties.
-
We use the
super
function to call the constructor of the parent class -
We can override member functions of the parent class with our own versions.
-
In member functions
super
refers to theparent
instance.
We can then instantiate this derived class like so:
let asim = new Student("Asim", "Hussain", "Angular 2");
console.log(asim.whoAreYou());
// Hi i'm Asim Hussain and i'm studying Angular 2
Access Modifiers
Everything we have learned so far about classes is pure ES6 Javascript.
However Typescript adds some nice functionality on top of ES6 classes, namely function and property visibility via access modifiers.
For example we can define the properties of our Person class as private
, like so:
class Person {
private firstName: string = "";
private lastName: string = "";
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
}
let asim = new Person("Asim","Hussain");
console.log(asim.firstName);
If we compiled the above with typescript it would print out this error:
error TS2341: Property 'firstName' is private and only accessible within class 'Person'.
By marking the firstName
property as private
it is now only visible from one of the methods on Person class.
We can also define class methods as private
with the same effect. If we tried to call a private
method from outside of a Person class, the typescript transpiler throws an error.
There are 3 access modifiers:
- public
-
This is the default and means its visible everywhere.
- private
-
Only member functions of the class it’s declared in can see this.
- protected
-
Only the class it’s declared in and any class that inherits from that class can see this.
Constructor shortcut
A really common pattern in constructors is to use them to initialise properties via arguments you pass into the constructor, like in our example:
class Person {
private firstName: string = "";
private lastName: string = "";
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
}
As long as you are using an access modifier Typescript lets us shorten this to:
class Person {
constructor(private firstName: string, private lastName: string) {
}
}
Interfaces
Typescript has another feature called an interface
. An interface can be used in a number of scenarios but by far the most common is when used wth classes.
When used with classes the syntax looks like this:
class Person implements Human {
}
Human
in the example above is an interface. An interface lets you describe the minimum set of public facing properties or methods that a class
has.
Another way interfaces are explained is that they describe a set of rules the class has to follow, a contract it has to adhere to.
So for us a Human
interface might look like:
interface Human {
firstName: string;
lastName: string;
}
Important
If the Person class then doesn’t implement at least a firstName
and a lastName
then typescript throws an error like so:
error TS2420: Class 'Person' incorrectly implements interface 'Human'. Property 'firstName' is missing in type 'Person'.
Sometimes however we might want an interface to describe an optional contract. We can append ?
to the name of the property or function to mark it as optional, like so:
interface Human {
firstName: string;
lastName: string;
name?: Function;
isLate?(time: Date): Function;
}
Summary
In ES6 we now have a new way of writing object oriented code with the class
syntax.
We can inherit methods and properties of one class into another by using the extends
keyword.
Under the hood we are still using prototype based inheritance but the syntax is easier to understand and more familiar for developers who are coming from other languages.
Typescript adds some extra functionality on-top of ES6 classes such as access modifiers and interfaces