Learn the fundamentals of Typescript classes in no time

In this post, I will be discussing TypeScript classes, which is a big topic in Object-Oriented Programming. To start, I will go over the essential features of TypeScript classes. In future posts, I will delve into the topic in greater detail and cover how TypeScript implements OOP concepts such as Inheritance and Polymorphism, as well as other topics such as Static, Generic, and Abstract classes. However, before we dive into those advanced topics, let’s first take a closer look at what a class is.

What are TypeScript classes?

TypeScript classes provide a way to define objects and their behavior in a structured and organized manner. With classes, you can create blueprints for objects with properties and methods, similar to traditional object-oriented programming languages. This helps to enforce strict typing and object-oriented programming principles, which can prevent common programming errors. 

TypeScript classes also support inheritance, which allows you to create new classes that inherit properties and methods from existing classes, as well as abstract classes for defining a common set of properties and methods for a group of related classes. By using TypeScript, you can build more complex object hierarchies and write maintainable code.

Since TypeScript is a superset of JavaScript, it fully supports the features of ECMAScript 2015 (ES6) and later versions, including classes. In fact, TypeScript extends the functionality of ES6 classes by adding features such as access modifiers, abstract classes, and interfaces. This enables developers to write code that is more type-safe and easier to maintain, while still taking advantage of the latest features in modern JavaScript.

How to create a class and instance ( object ) in TypeScript

A class is created with a class keyword as follows.

class Vehicle {
  // properties
  make: string;
  model: string;
  
  // constructor method
  constructor(make: string, model: string) {
    this.make = make;
    this.model = model;
  }
  
  // methods
  startEngine() {
    console.log(`Starting the engine of a ${this.make} ${this.model}...`);
  }
  
  stopEngine() {
    console.log(`Stopping the engine of a ${this.make} ${this.model}...`);
  }
}

Now to create an instance of the class, you need to use the new keyword followed by the class name
const myCar = new Vehicle('Honda', 'Civic');

What is a Constructor ( method )

  • A constructor is a special method that is called when an instance of a class is created using the new keyword.
  • The constructor method does not have a separate name and is defined using the constructor keyword.
  • The constructor can take parameters. Objects are initialized with these parameters when they are created.

const myCar = new Vehicle('Honda', 'Civic');

  • TypeScript supports parameter properties, which allow you to declare and initialize a class property in a single line of code in the constructor parameter list.

Therefore, you can rewrite the above constructor as

class Vehicle {
  /* 
     No need to specify properties separately in the class
     The properties are automatically created based on the constructor parameters
 */

  constructor(public make: string, public model: string, public year: number) {
        // No need to add constructor body
  }

  /* other member functions */

}

  • TypeScript constructors can have access modifiers (public, private, or protected) that control the visibility of the constructor and its parameters.
  • The super() keyword is used to call the constructor of a parent class from a subclass constructor( we’ll be discussing this topic in a future topic on inheritance ).
  • If a class does not have an explicit constructor, TypeScript generates a default constructor that takes no arguments and does nothing.
  • Constructors can include logic and perform operations such as initializing fields, setting up data structures, and performing validation.

Despite having almost the same function signature as any other typescript function, the constructor has two key differences.

  1. Constructors should not have type parameters

TypeScript, type parameters belong on the outer class declaration, not the constructor. This is because the constructor is only called when an object of the class is created, whereas type parameters apply to the class as a whole. Therefore, any type parameter for a class must be specified on the class declaration itself. ( you will understand more about type parameters when we talk about Generic Classes )

class Vehicle {
  constructor<T>(public make: string, public model: string) {}
}

// This will result in a compilation error:
const myVehicle = new Vehicle<string>("Honda", "Civic");
  1. Constructors should not have return type annotations

The constructor is used to initialize the object of the class, so its return type is always the instance type of the class. It is not necessary to specify a return type for the constructor, because TypeScript automatically infers the return type to be the instance type of the class. In fact, attempting to specify a return type for the constructor will result in a compilation error.

Member Visibility of TypeScript classes ( Access modifiers )

There are three access modifiers that can restrict access to members of a class.

  1. Public

By default, every member of a class in object-oriented programming has public access level. This means that every member of a class can be accessed from outside the declaration, regardless of whether it is explicitly specified. A class’s methods are often declared public to allow external access to the class’s behavior, while properties are usually kept private to restrict direct access to the class’s data. By controlling the visibility of a class’s members, you can ensure that the class is used correctly and safely.

  1. Private

private keyword is used to specify that a class member ( field or method) can only be accessed within the same class where it is declared. Methods are usually kept public so that they can be used to access private properties indirectly. By accessing properties through public methods, we can ensure that the data is accessed and modified in a controlled and safe manner.

  1. Protected

By using the protected keyword, the access level of members is changed so that they can be directly accessed by subclasses while being restricted from access outside of the class.

The “protected” keyword is commonly used in implementing inheritance in object-oriented programming, as it allows subclasses( derived classes) to access the protected members of their parent class, while still restricting access to those members from outside the class hierarchy.

TypeScript Getters ( Accessors ) and Setters ( mutators)

Getters and setters are unique methods that enable you to read and write private or public fields with supplementary logic or validation in TypeScript. To define a getter, you can use the get keyword followed by a method name, and for a setter, you can use the set keyword followed by a method name.

For example, the Vehicle class with accessors is as below

class Vehicle {
  private _make: string;
  private _model: string;

  constructor(make: string, model: string) {
    this._make = make;
    this._model = model;
  }

  get make(): string {
    return this._make;
  }

  set make(make: string) {
    this._make = make;
  }

  get model(): string {
    return this._model;
  }

  set model(model: string) {
    this._model = model;
  }

  // methods
  startEngine() {
    console.log(`Starting the engine of a ${this.make} ${this.model}...`);
  }
  
  stopEngine() {
    console.log(`Stopping the engine of a ${this.make} ${this.model}...`);
  }
}

now you can make calls to getters and setters as below

const car = new Vehicle("Toyota", "Corolla");

//calls to getters
console.log(car.make); // "Toyota"
console.log(car.model); // "Corolla"

//use setters to assigned values to properties of the object
car.make = "Honda";
car.model = "Accord";

//calls to getters
console.log(car.make); // "Honda"
console.log(car.model); // "Accord"

TypeScript makes inferences about class properties based on the usage of getters and setters.

  • If a class has a getter but no setter, the property is automatically inferred as readonly
class Vehicle {
  private _topSpeed: number = 0;//A readonly property must be initialized on the declaration. 

  public get topSpeed(): number {
    return this._topSpeed;
  }

  public setTopSpeed(speed: number): void {
    this._topSpeed = speed;
  }
}

const myCar = new Vehicle();
myCar.setTopSpeed(200); // Set the top speed of the car to 200 mph
console.log(myCar.topSpeed); // Output: 200

// Attempting to set the value of topSpeed directly will result in a compile-time error:
myCar.topSpeed = 250; // Error: Cannot assign to 'topSpeed' because it is a read-only property.
 
  • If the type of the setter is not specified, it is inferred from the return type of the getter
class Vehicle {
  private _topSpeed: number = 0;

  public get topSpeed(): number {
    return this._topSpeed;
  }

  public set topSpeed(speed) {
    this._topSpeed = speed;
  }
}

const myCar = new Vehicle();
myCar.topSpeed = 200; // Set the top speed of the car to 200 mph
console.log(myCar.topSpeed); // Output: 200

myCar.topSpeed = "not a number"; // Error: Type '"not a number"' is not assignable to type 'number'

Conclusion

I trust that you have acquired a sound grasp of creating TypeScript classes and generating instances from those classes. This post is merely the start of a sequence of articles on TypeScript that I aspire to offer for your enlightenment and professional growth. Please don’t hesitate to leave your thoughts in the comment section below, as I value your feedback and insights as much as I do online resources.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top