🔷 TypeScript Q27 / 101

Explain class implementation in TypeScript.

AI-Powered Answer ✓ Answered

TypeScript brings robust object-oriented programming (OOP) capabilities to JavaScript through its enhanced support for classes. Building upon ECMAScript 2015 (ES6) classes, TypeScript adds powerful features like static typing, access modifiers, and interfaces, making it easier to build scalable and maintainable applications.

Core Concepts of TypeScript Classes

Classes in TypeScript serve as blueprints for creating objects (instances). They encapsulate data (properties) and behavior (methods) into a single unit, promoting modularity and reusability. TypeScript's type system ensures that class members are used correctly, catching potential errors at compile-time rather than runtime.

Basic Class Structure

A class is defined using the class keyword, followed by the class name. It can contain properties (variables) and methods (functions) that define the state and behavior of objects created from it.

typescript
class Greeter {
  message: string; // Property

  constructor(message: string) {
    this.message = message;
  }

  greet() { // Method
    console.log(this.message);
  }
}

let greeter = new Greeter("Hello, TypeScript!");
greeter.greet(); // Output: Hello, TypeScript!

Constructors

The constructor is a special method that is called when a new instance of the class is created using the new keyword. It's primarily used to initialize class properties. TypeScript allows a shorthand for declaring and initializing properties directly in the constructor signature, especially with access modifiers.

typescript
class Point {
  // Shorthand: public x and y are declared and initialized
  constructor(public x: number, public y: number) {}

  getDistance() {
    return Math.sqrt(this.x * this.x + this.y * this.y);
  }
}

let p = new Point(3, 4);
console.log(p.x); // Output: 3
console.log(p.getDistance()); // Output: 5

Access Modifiers: public, private, protected

TypeScript provides access modifiers to control the visibility and accessibility of class members (properties and methods):

  • public: Members are accessible from anywhere. This is the default modifier if none is specified.
  • private: Members are only accessible from within the class they are declared in.
  • protected: Members are accessible from within the class itself and from subclasses (derived classes).
typescript
class Animal {
  public name: string;
  private age: number;
  protected species: string;

  constructor(name: string, age: number, species: string) {
    this.name = name;
    this.age = age;
    this.species = species;
  }

  describe() {
    console.log(`This is a ${this.species} named ${this.name}.`);
    // console.log(this.age); // Accessible here
  }
}

class Dog extends Animal {
  constructor(name: string, age: number) {
    super(name, age, "Canine");
    console.log(`Dog's species: ${this.species}`); // Protected member accessible in subclass
    // console.log(this.age); // Error: Property 'age' is private and only accessible within class 'Animal'.
  }
}

let myAnimal = new Animal("Leo", 5, "Lion");
console.log(myAnimal.name); // Accessible: 'Leo'
// console.log(myAnimal.age); // Error: Property 'age' is private
// console.log(myAnimal.species); // Error: Property 'species' is protected

Inheritance

TypeScript supports inheritance, allowing a class (subclass or derived class) to extend another class (superclass or base class) and inherit its properties and methods. The extends keyword is used for this purpose, and super() is used in the subclass's constructor to call the base class's constructor.

typescript
class Vehicle {
  constructor(public wheels: number) {}

  move(): void {
    console.log("Vehicle is moving.");
  }
}

class Car extends Vehicle {
  constructor(public make: string, wheels: number) {
    super(wheels); // Call the base class constructor
  }

  drive(): void {
    console.log(`The ${this.make} car with ${this.wheels} wheels is driving.`);
  }
}

let myCar = new Car("Tesla", 4);
myCar.move(); // Inherited method
myCar.drive(); // Own method

Implementing Interfaces

Classes can implement interfaces, which means they must adhere to the contract defined by the interface. This ensures that the class provides specific properties and methods, promoting consistent structure across different classes.

typescript
interface ILogger {
  log(message: string): void;
  warn(message: string): void;
}

class ConsoleLogger implements ILogger {
  log(message: string): void {
    console.log(`LOG: ${message}`);
  }
  warn(message: string): void {
    console.warn(`WARN: ${message}`);
  }
}

let logger: ILogger = new ConsoleLogger();
logger.log("This is a log message.");

Abstract Classes

Abstract classes are base classes from which other classes may be derived. They cannot be instantiated directly. They can contain abstract methods (methods declared without an implementation) that must be implemented by non-abstract derived classes, enforcing a common structure and behavior.

typescript
abstract class Shape {
  constructor(public color: string) {}

  abstract calculateArea(): number; // Abstract method, no implementation here

  displayColor(): void {
    console.log(`This shape is ${this.color}.`);
  }
}

class Circle extends Shape {
  constructor(color: string, public radius: number) {
    super(color);
  }

  calculateArea(): number { // Implementation of abstract method
    return Math.PI * this.radius * this.radius;
  }
}

// let myShape = new Shape("blue"); // Error: Cannot create an instance of an abstract class.
let myCircle = new Circle("red", 10);
console.log(myCircle.calculateArea());
myCircle.displayColor();

Static Members

Static members (properties and methods) belong to the class itself, not to instances of the class. They are accessed directly on the class name, rather than on an object instance. They are useful for utility functions or properties that are shared across all instances or don't require an instance state.

typescript
class MathUtils {
  static PI: number = 3.14159; // Static property

  static calculateCircumference(radius: number): number { // Static method
    return 2 * MathUtils.PI * radius;
  }
}

console.log(MathUtils.PI); // Access static property directly
console.log(MathUtils.calculateCircumference(5)); // Access static method directly

In conclusion, TypeScript's class implementation provides a robust and type-safe way to build object-oriented applications. By leveraging features like access modifiers, inheritance, interfaces, abstract classes, and static members, developers can create well-structured, maintainable, and scalable codebases.