1. Object-oriented Programming
As we know JavaScript
does'nt have the concept of classes
like other programming languages such as ( PHP, Java, C++, C# ... ).
with ES6
you can defined classes
but it's just a syntactic sugar for creating constructor function
and prototypal inheritance
.
Let's see OOP
in TypeScript
:
1- Creating Classes and objects :
class Account {
id: number;
owner: string;
balance: number;
constructor(id: number, owner: string, balance: number) {
this.id = id;
this.owner = owner;
this.balance = balance;
}
deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
}
throw new Error("Invalid amount");
}
}
let account = new Account(1, "zineddine", 100);
account.deposit(100);
console.log(typeof account); // object
console.log(account instanceof Account); // true
/*
always make sure to use instanceof property to check if
an object is an instance of a class
*/
Note :
You can't use the function
keyword inside a class
to declare a function
, use it only when you declare a stand-alone function
.
2- Read-only and Optional Properties :
class User {
readonly id: number;
name: string;
email: string;
nickname?: string; // optional property
constructor(id: number, name: string, email: string) {
this.id = id;
this.name = name;
this.email = email;
}
}
let user = new User(1, "zineddine", "hz_haddad@esi.dz");
user.id = 12; // Cannot assign to 'id' because it is a read-only property
3- Access Control Keywords :
class Account {
/*
public # by default
private
protected
*/
id: number;
private _balance: number;
constructor(id: number, balance: number) {
this.id = id;
this._balance = balance;
}
deposit(amount: number): void {
if (amount > 0) {
// assume we want also to log the transaction
this._balance += amount;
}
throw new Error("Invalid amount");
}
private calculateTax(amount: number): number {
return amount * 0.1;
}
getBalance(): number {
return this._balance;
}
}
let account = new Account(1, 100);
account._balance -= 50; // Property '_balance' is private and only accessible within class 'Account'
4- Parameter Properties and Getters & Setters :
class Account {
nickname?: string; // optional property
constructor(
public readonly id: number,
public owner: string,
private _balance: number
) {}
get balance(): number {
return this._balance;
}
set balance(value: number) {
if (value < 0) {
throw new Error("Balance cannot be negative");
}
this._balance = value;
}
}
let account = new Account(1, "zineddine", 100);
console.log(account.balance); // 100
account.balance = -100; // throws error
account.balance = 100; // OK
5- Index Signatures :
Index Signatures are just a fancy name for dynamic properties
class NameByNumber {
// index signature property
[name: string]: number;
}
let nameByNumber = new NameByNumber();
nameByNumber.John = 1;
// nameByNumber.['John'] = 1;
// nameByNumber.John = '1'; Type 'string' is not assignable to type 'number'
nameByNumber.Jane = 2;
nameByNumber.Bob = 3;
console.log(nameByNumber.John); // 1
6- Static Members :
class Ride {
private static _activeRides: number = 0;
start() {
Ride._activeRides++;
}
end() {
Ride._activeRides--;
}
static get activeRides() {
return Ride._activeRides;
}
}
let ride1 = new Ride();
let ride2 = new Ride();
ride1.start();
ride2.start();
console.log(Ride.activeRides); // 2
7- Inheritance and Methods Overriding :
class Person {
constructor(public firstName: string, public lastName: string) {}
get fullName() {
return this.firstName + " " + this.lastName;
}
walk() {
console.log("Walking");
}
}
class Student extends Person {
constructor(firstName: string, lastName: string, public id: number) {
super(firstName, lastName);
}
override walk() {
super.walk();
console.log("Walking on the stairs");
}
override get fullName() {
return "Student : " + super.fullName;
}
}
let student = new Student("John", "Doe", 123);
console.log(student.fullName);
student.walk();
/*
Walking
Walking on the stairs
*/
console.log(student instanceof Person); // true
8- Polymorphism :
// parent class , base class , super class
class Person {
protected steps: number = 0;
constructor(public firstName: string, public lastName: string) {}
get fullName() {
return this.firstName + " " + this.lastName;
}
}
// child class , sub class , derived class
class Student extends Person {
constructor(firstName: string, lastName: string, public id: number) {
super(firstName, lastName);
}
override get fullName() {
return "Student : " + super.fullName;
}
}
class Teacher extends Person {
constructor(firstName: string, lastName: string, public id: number) {
super(firstName, lastName);
}
override get fullName() {
return "Teacher : " + super.fullName;
}
}
function printName(persons: Person[]) {
for (let person of persons) {
console.log(person.fullName);
}
}
printName([
new Person("John", "Doe"),
new Student("Jane", "Doe", 123),
new Teacher("John", "Doe", 123),
]);
/*
John Doe
Student : Jane Doe
Teacher : John Doe
*/
9- Abstract Classes :
abstract class Shape {
constructor(public color: string) {}
abstract render(): void;
}
class Circle extends Shape {
constructor(public radius: number, color: string) {
super(color);
}
override render(): void {
console.log("Circle");
}
}
let shape = new Shape("red"); // Cannot create an instance of an abstract class
10- Interfaces :
interface Calender {
name: string;
addEvent(event: string): void;
removeEvent(event: string): void;
}
interface CloudCalender extends Calender {
sync(): void;
}
class GoogleCalender implements CloudCalendar {
constructor(public name: string) {}
addEvent(event: string): void {
console.log(`Adding ${event} to GoogleCalendar`);
}
removeEvent(event: string): void {
console.log(`Removing ${event} from GoogleCalendar`);
}
sync(): void {
console.log("Syncing GoogleCalendar");
}
}
Note :
In TypeScript
, interfaces
and type aliases
can be used interchangeably.
Both can be used to describe the shape of an object
interface Person {
name: string;
}
let person: Person = {
name: "Zineddine",
};
type User = {
name: string;
};
let user: User = {
name: "Zineddine",
};
That’s it for the this chapter !
Github link : TypeScript-Fundamentals-in-One-Place
Top comments (0)