Classes
Blueprints for creating objects. Classes encapsulate properties (variables) and methods (functions). this
refers to the object instance
class Person {
public name: string; // accessible from anywhere
protected age: number; // accessible from the class and subclasses
private secret: string = "shh!"; // only accessible within the class
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hi, I'm ${this.name}`;
}
get secret(): string {
return this.secret;
}
}
const person = new Person("Alice", 25);
console.log(person.greet()); // Hi, I'm Alice
console.log(person.secret); // shh!
Methods
Different types of methods include: instance, static, and accessor (getters/setters) methods
class Person {
private _age: number;
constructor(public name: string, age: number) {
this._age = age;
}
// instance methods belong to the instance
greet(): string {
return `Hi, I'm ${this.name}`;
}
// static methods belong to the class
static species(): string {
return "Homo sapiens";
}
// getter
get age(): number {
return this._age;
}
// setter
set age(newAge: number) {
// validation
if (newAge > 0) {
this._age = newAge;
} else {
console.log("Age must be a positive number");
}
}
}
const person = new Person("Alice", 25);
console.log(person.greet()); // Hi, I'm Alice
console.log(Person.species()); // Homo sapiens
console.log(person.age); // using the age getter, 25
person.age = 30; // using the age setter
console.log(person.age); // 30
person.age = -5; // Age must be a positive number
Inheritance & Polymorphism
Inheritance allows one class to extend another, inheriting its properties and methods. Use the super
keyword to pass args to the parent class
Polymorphism allows subclasses to override inherited methods with their own implementation
class Animal {
constructor(species: string) {}
speak(): string {
return "Some generic sound";
}
}
class Dog extends Animal {
constructor(name: string) {
super("Dog");
}
// overriding the Animal speak method
speak(): string {
return "Bark";
}
}
const dog = new Dog("Buddy");
console.log(dog.species, dog.name); // Dog Buddy
console.log(dog.speak()); // Bark
Abstract Classes
Abstract classes cannot be instantiated directly and serve as blueprints for other classes
abstract class Vehicle {
constructor(public model: string) {}
// abstract method
// must be implemented by subclasses
abstract move(): void;
info(): string {
return `This is a ${this.model}`;
}
}
class Car extends Vehicle {
move(): void {
console.log(`${this.model} is driving.`);
}
}
const car = new Car("Sedan");
car.move(); // Sedan is driving.
console.log(car.info()); // This is a Sedan
Extending Built-in Classes
You can extend built-in classes (like Array
, Map
, or Set
) to add or modify behavior
class NumericArray extends Array<number> {
push(...items: number[]): number {
for (const item of items) {
if (typeof item !== "number") {
throw new Error("Only numbers are allowed!");
}
}
return super.push(...items);
}
}
const nums = new NumericArray();
nums.push(1, 2, 3);
console.log(nums); // [1, 2, 3]
Design Patterns
Singleton
Restrict a class to a single instance
class Singleton {
private static instance: Singleton;
private constructor(public value: number) {}
static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton(42);
}
return Singleton.instance;
}
}
const singleton1 = Singleton.getInstance();
const singleton2 = Singleton.getInstance();
console.log(singleton1 === singleton2); // true
Factory
Instantiate classes without exposing the creation details
abstract class Shape {
abstract draw(): void;
}
class Circle extends Shape {
draw(): void {
console.log("Drawing a circle.");
}
}
class Square extends Shape {
draw(): void {
console.log("Drawing a square.");
}
}
class ShapeFactory {
static createShape(type: "circle" | "square"): Shape {
switch (type) {
case "circle":
return new Circle();
case "square":
return new Square();
}
}
}
const shape1 = ShapeFactory.createShape("circle");
const shape2 = ShapeFactory.createShape("square");
shape1.draw(); // Drawing a circle.
shape2.draw(); // Drawing a square.
Closures
Functions that capture variables from their surrounding scope
function createCounter() {
let count = 0;
return {
increment() {
count++;
console.log(count);
},
decrement() {
count--;
console.log(count);
},
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1