TypeScript if…else Statement

TypeScript, classes are a fundamental component of object-oriented programming, serving as blueprints for creating objects with shared properties and behavior. Classes encapsulate data (properties) and operations (methods) within a single unit, promoting code organization, reusability, and maintainability. They support features like inheritance, encapsulation, and polymorphism, facilitating the creation of hierarchies of related objects.

TypeScript enhances the JavaScript class syntax by introducing type annotations, access modifiers, abstract classes, and interfaces, providing developers with a robust toolset for building scalable and type-safe applications. Class instances are created using the new keyword, and class hierarchies contribute to the construction of complex software systems in a structured and modular manner.

Defining Classes

class Person { // Properties with types name: string; age: number; // Constructor to initialize properties constructor(name: string, age: number) { this.name = name; this.age = age; } // Methods with typed parameters and return values greet(): string { return `Hello, I'm ${this.name}!`; } } // Creating an object instance const john = new Person("John", 30); // Accessing properties and methods console.log(john.name); // logs "John" console.log(john.greet()); // logs "Hello, I'm John!"

This example defines a Person class with name and age properties typed as string and number, respectively. The constructor initializes these properties based on arguments passed during object creation. Additionally, a greet method is defined to return a greeting message based on the name property.

Access Modifiers

TypeScript classes let you control access to members using keywords like public (accessible anywhere), private (accessible only within the class), and protected (accessible within the class and subclasses).

class Employee extends Person { // Private salary property accessible only within the class private _salary: number; constructor(name: string, age: number, salary: number) { super(name, age); // Calling parent constructor this._salary = salary; } // Public get method to access salary indirectly get salary(): number { return this._salary; } // Protected raiseSalary method accessible to subclasses protected raiseSalary(amount: number): void { this._salary += amount; } } // Accessing public members const jane = new Employee("Jane", 25, 50000); console.log(jane.name); // logs "Jane" console.log(jane.salary); // logs 50000 // Cannot directly access private property // console.log(jane._salary); // Error: Property '_salary' is private // Using protected method in subclass class Manager extends Employee { public bonus(): number { this.raiseSalary(1000); // Accessing protected method return this.salary * 0.1; } }

This example demonstrates access modifiers. The _salary property in Employee is private, so it's not directly accessible. Instead, a public get method provides controlled access. raiseSalary is protected, allowing access in subclasses like Manager, which uses it to calculate a bonus.

Inheritance

Classes can inherit properties and methods from other classes using the extends keyword. This allows building specialized subclasses with additional functionality.

class Doctor extends Person { specialty: string; constructor(name: string, age: number, specialty: string) { super(name, age); this.specialty = specialty; } diagnose(): string { return `Dr. ${this.name} is examining you.`; } } const drMark = new Doctor("Mark", 40, "Cardiology"); console.log(drMark.greet()); // inherited from Person console.log(drMark.diagnose()); // specific to Doctor

Here, Doctor inherits from Person and adds a specialty property and a diagnose method. This shows how inheritance promotes code reuse and specialization.

Advanced Constructors

Optional Parameters: Define parameters with default values for a more flexible initialization.
class Product { constructor( public name: string, public price: number, public brand = "Acme" // Default brand ) {} } const chair = new Product("Comfy Chair", 100); // Only name and price provided console.log(chair.brand); // logs "Acme"
Rest Parameters: Capture an unlimited number of remaining arguments into an array.
class ShoppingCart { constructor(...items: string[]) { this.items = items; } addItem(newItem: string) { this.items.push(newItem); } } const cart = new ShoppingCart("Apples", "Bananas", "Cheese"); cart.addItem("Bread"); console.log(cart.items); // logs ["Apples", "Bananas", "Cheese", "Bread"]

Static Members

Define properties and methods directly attached to the class itself, not to individual instances.

class Point { static origin = { x: 0, y: 0 }; constructor(public x: number, public y: number) {} static distance(p1: Point, p2: Point): number { const deltaX = p2.x - p1.x; const deltaY = p2.y - p1.y; return Math.sqrt(deltaX ** 2 + deltaY ** 2); } } const point1 = new Point(5, 10); console.log(Point.origin); // logs { x: 0, y: 0 } console.log(Point.distance(point1, { x: 3, y: 4 })); // calculates distance from point1

Generics

Create classes that work with different types of data at runtime, adding flexibility and robustness.

class Queue<T> { private items: T[] = []; enqueue(item: T) { this.items.push(item); } dequeue(): T undefined { return this.items.shift(); } } const numberQueue = new Queue<number>(); numberQueue.enqueue(10); numberQueue.enqueue(20); const stringQueue = new Queue<string>(); stringQueue.enqueue("Hello"); stringQueue.enqueue("World");

This example shows two queues, one for numbers and one for strings, using the same Queue class with generics.

Abstract Classes

Define blueprints for subclasses but leave some methods with no implementation, forcing subclasses to provide their own.

abstract class Shape { abstract area(): number; // Must be implemented in subclasses perimeter(): number { // ... implementation based on specific shape } } class Circle extends Shape { constructor(public radius: number) {} area(): number { return Math.PI * this.radius * this.radius; } perimeter(): number { return 2 * Math.PI * this.radius; // Using parent method and adding specific calculation } } const circle = new Circle(5); console.log(circle.area()); // logs 78.53981633974483 console.log(circle.perimeter()); // logs 31.41592653589793

Decorators

Enhance classes with additional functionality at runtime.

function logClass(target: any) { console.log(`Class decorated: ${target.name}`); } @logClass class User { name: string; constructor(name: string) { this.name = name; } } // logs "Class decorated: User" at runtime

This example shows a decorator logging the class name when it's decorated with the @logClass annotation.

Conclusion

Classes serve as blueprints for creating objects, encapsulating both data and behavior within a single structure. They support features such as access modifiers for controlling member visibility, inheritance to facilitate code reuse, and abstract classes/interfaces for defining common blueprints.