Transitioning to TypeScript: The Ultimate Starter Guide - Part 2

Unlocking the Power of Static Typing and Enhanced Code Maintainability

TypeScript from JavaScript: A Comprehensive Guide - Part 2 of 7

Hello again, future TypeScript pros! 🌟

In the first part of this series, we introduced you to the basics of TypeScript, its advantages over JavaScript, and how to set up a TypeScript project. In this second part, we will dive deeper into TypeScript's type system and learn about more advanced features like interfaces, classes, and generics. Let's get started!

Table of Contents

  1. Recap

  2. Interfaces in TypeScript

  3. Classes in TypeScript

  4. Generics in TypeScript

  5. Conclusion

1. Recap

In the first part of this series, we learned about some basic types in TypeScript like number, string, boolean, array, tuple, enum, any, and void. We also learned how to set up a TypeScript project and compile TypeScript code into JavaScript.

2. Interfaces in TypeScript

Interfaces are a powerful way to define contracts within your code and contracts with code outside of your project. They are used to define the shape of an object, i.e., the properties and methods that an object should have.

a. Defining Interfaces

You can define an interface using the interface keyword followed by the interface name. Here is an example of a simple interface:

interface Person {
  firstName: string;
  lastName: string;
}

In this example, the Person interface has two properties: firstName and lastName, both of which are of type string.

b. Implementing Interfaces

Once you have defined an interface, you can use it as a type for your variables, parameters, or return values. Here is an example:

function greet(person: Person) {
  return `Hello, ${person.firstName} ${person.lastName}`;
}

const john: Person = {
  firstName: 'John',
  lastName: 'Doe',
};

console.log(greet(john)); // Hello, John Doe

In this example, the greet function expects a Person object as its parameter. The john object is of type Person, so it can be passed to the greet function.

c. Optional Properties

Interfaces can also have optional properties. You can mark a property as optional by adding a ? after its name. Here is an example:

interface Person {
  firstName: string;
  lastName: string;
  middleName?: string;
}

const john: Person = {
  firstName: 'John',
  lastName: 'Doe',
};

const jane: Person = {
  firstName: 'Jane',
  lastName: 'Doe',
  middleName: 'Mary',
};

In this example, the middleName property is optional. The john object does not have a middleName property, and that's fine. The jane object has a middleName property, and that's fine too.

d. Read-Only Properties

Interfaces can also have read-only properties. You can mark a property as read-only by adding the readonly keyword before its name. Here is an example:

interface Person {
  readonly id: number;
  firstName: string;
  lastName: string;
}

const john: Person = {
  id: 1,
  firstName: 'John',
  lastName: 'Doe',
};

john.id = 2; // Error: Cannot assign to 'id' because it is a read-only property

In this example, the id property is read-only. Trying to modify it after its initial assignment will result in a compile-time error.

3. Classes in TypeScript

Classes in TypeScript are similar to classes in other object-oriented programming languages. They are used to define the blueprint for creating objects.

a. Defining Classes

You can define a class using the class keyword followed by the class name. Here is an example of a simple class:

class Car {
  brand: string;
  model: string;
  year: number;

  constructor(brand: string, model: string, year: number) {
    this.brand = brand;
    this.model = model;
    this.year = year;
  }

  start() {
    console.log('Car started');
  }

  drive() {
    console.log('Car is driving');
  }
}

In this example, the Car class has three properties: brand, model, and year, and three methods: constructor, start, and drive.

b. Creating Objects

Once you have defined a class, you can create objects of that class using the new keyword. Here is an example:

const myCar = new Car('Toyota', 'Corolla', 2020);

myCar.start();
myCar.drive();

In this example, a new Car object is created with the brand, model, and year properties set to 'Toyota', 'Corolla', and 2020, respectively. The start and drive methods are then called on the myCar object.

c. Access Modifiers

TypeScript supports three access modifiers: public, private, and

protected.

  • public: This is the default modifier and can be accessed anywhere.

  • private: This can only be accessed within the class where it is defined.

  • protected: This can be accessed within the class where it is defined and in any subclass of that class.

Here is an example:

class Car {
  public brand: string;
  private model: string;
  protected year: number;

  constructor(brand: string, model: string, year: number) {
    this.brand = brand;
    this.model = model;
    this.year = year;
  }
}

const myCar = new Car('Toyota', 'Corolla', 2020);

console.log(myCar.brand); // Toyota
console.log(myCar.model); // Error: Property 'model' is private and only accessible within class 'Car'
console.log(myCar.year); // Error: Property 'year' is protected and only accessible within class 'Car' and its subclasses

In this example, the brand property can be accessed outside of the Car class because it is public. The model and year properties cannot be accessed outside of the Car class because they are private and protected, respectively.

d. Inheritance

Classes in TypeScript can inherit properties and methods from other classes. This is done using the extends keyword. Here is an example:

class ElectricCar extends Car {
  range: number;

  constructor(brand: string, model: string, year: number, range: number) {
    super(brand, model, year);
    this.range = range;
  }

  charge() {
    console.log('Car is charging');
  }
}

const myElectricCar = new ElectricCar('Tesla', 'Model 3', 2020, 500);

myElectricCar.start();
myElectricCar.drive();
myElectricCar.charge();

In this example, the ElectricCar class extends the Car class and inherits its properties and methods. It also adds a new property, range, and a new method, charge.

4. Generics in TypeScript

Generics are a way to create reusable components that work with any type. They are similar to templates in other programming languages.

a. Generic Functions

You can create generic functions by using a type variable, which is a special kind of variable that works on types rather than values. Here is an example:

function identity<T>(arg: T): T {
  return arg;
}

const myNumber = identity<number>(5);
const myString = identity<string>('Hello');

console.log(myNumber); // 5
console.log(myString); // Hello

In this example, the identity function is a generic function that takes an argument arg of type T and returns a value of type T. The myNumber and myString variables are created by calling the identity function with <number> and <string> type arguments, respectively.

b. Generic Classes

You can also create generic classes by using a type variable. Here is an example:

class ArrayOfNumbers {
  private items: number[];

  constructor() {
    this.items = [];
  }

  add(item: number) {
    this.items.push(item);
  }

  getItems() {
    return this.items;
  }
}

const numbers = new ArrayOfNumbers();
numbers.add(1);
numbers.add(2);
numbers.add(3);
console.log(numbers.getItems()); // [1, 2, 3]

class ArrayOfStrings {
  private items: string[];

  constructor() {
    this.items = [];
  }

  add(item: string) {
    this.items.push(item);
  }

  getItems() {
    return this.items;
  }
}

const strings = new ArrayOfStrings();
strings.add('a');
strings.add('b');
strings.add('c');
console.log(strings.getItems()); // ['a', 'b', 'c']

In this example, the ArrayOfNumbers and ArrayOfStrings classes are not generic, and therefore, you need to create two separate classes for handling arrays of number and string items. This is not very efficient.

Here is how you can use generics to create a single Array class that can handle arrays of any type:

class Array<T> {
  private items: T[];

  constructor() {
    this.items = [];
  }

  add(item: T) {
    this.items.push(item);
  }

  getItems() {
    return this.items;
  }
}

const numbers = new Array<number>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
console.log(numbers.getItems()); // [1, 2, 3]

const strings = new Array<string>();
strings.add('a');
strings.add('b');
strings.add('c');
console.log(strings.getItems()); // ['a', 'b', 'c']

In this example, the Array class is a generic class that takes a type argument T. The numbers and strings variables are created by calling the Array class with <number> and <string> type arguments, respectively.

5. Conclusion

Congrats! 🎉 You have successfully learned about interfaces, classes, and generics in TypeScript. These are some of the most powerful features of TypeScript and will help you write more robust

and reusable code.

In the next part of this series, we will learn about modules, namespaces, and decorators in TypeScript. Stay tuned!

That's all for now, happy coding! 💻✨


If you are just joining in, here are the links to the other parts of this series:

  1. TypeScript from JavaScript Part 1: Introduction and Basic Types

That’s a wrap. Thanks for reading.

If you're looking for more premium content and resources to help you start and grow your business, consider subscribing to my Newsletter.

Want to see what I am working on? Check out my Twitter

Did you find this article valuable?

Support Innovate Sphere by becoming a sponsor. Any amount is appreciated!