Mastering TypeScript: From Beginner to Advanced


TypeScript has taken the web development world by storm, and for good reason. In this article, we’ll dive into the key aspects of TypeScript, including why it’s becoming a must-have tool for developers, how it improves JavaScript, and explore some of the advanced features that help make your code robust and maintainable.

Why Use TypeScript?

Think of JavaScript as a fast, reliable car—one that gets you from point A to B. But if something goes wrong, you don’t find out until you’re halfway through your journey, which can lead to a breakdown. TypeScript, on the other hand, is like a car equipped with advanced sensors. It warns you about potential issues before you even start the engine.

In other words, TypeScript enhances JavaScript by adding static types, giving you a layer of security and reliability. Developers catch errors early in the development process, making applications smoother, more stable, and less likely to break later.

How TypeScript is Helpful Over JavaScript?

Here are a few reasons TypeScript improves JavaScript development:

  • Static Typing: TypeScript adds static types, helping you catch errors while writing code instead of at runtime.

  • Early Error Detection: By checking for issues upfront, it saves debugging time and reduces bugs significantly.

  • Improved Readability & Maintainability: Autocompletion, better documentation, and refactoring tools make managing your codebase easier.

  • Enforces Code Structure: TypeScript ensures that functions, variables, and objects are used correctly, leading to more reliable and scalable code.

In short, using TypeScript ensures better code quality and long-term maintainability, especially as projects grow larger.

Is TypeScript Slower than JavaScript?

A common myth is that TypeScript adds overhead and is slower than JavaScript because of its additional compilation step. However, this is only a development-phase concern. Once TypeScript code is compiled to JavaScript, the runtime performance is exactly the same. So, TypeScript applications perform just as fast as pure JavaScript applications.

Getting Started with TypeScript: Setup

Setting up TypeScript is straightforward. Here’s how you can get started:

  1. Install Node.js (if you haven't already)

  2. Install TypeScript globally:
    Run npm install typescript -g

  3. Create a TypeScript config file:
    Run tsc --init to set up the TypeScript configuration file.

  4. Compile a TypeScript file:
    Use tsc script.ts to compile a TypeScript file into JavaScript.

  5. Watch for changes:
    Use tsc -watch to continuously compile changes in real time.

Essential TypeScript Code Snippets

Here are some important concepts and code examples to get you going with TypeScript.

Types

Types help define what kind of data a variable or function can hold or return.

  • Basic Types (Number, String, Boolean):

      let a: (number|string)[];
      a = [5, 5, 4];
      a.push(5);
      a.push('app');
    
      function abc() {
          console.log(a);
      }
    
      abc();
    
  • Any Type: Avoid using any as much as possible, as it defeats the purpose of TypeScript.

      let a: any[];
      a = [5, 5, 4];
      a.push(5);
      a.push('app');
    
      function abc() {
          console.log(a);
      }
    
      abc();
    
  • Unknown Type: Similar to any but ensures you must validate the type before using it.

  • Never Type: Functions that never return, often used for errors or infinite loops.

      function run(): never {
          while(true) {
              console.log('Running...');
          }
      }
      run(); // The code will not run anything after this.
    
  • Void Type: Functions that do not return any value.

      function fun(): void {
          console.log('This returns nothing');
      }
    
      fun();
    
  • Enums: Enums allow you to define a set of named constants.

      enum Direction {
          Up = "UP",
          Down = "DOWN",
          Left = "LEFT",
          Right = "RIGHT"
      }
    
      console.log(Direction.Up);  // Output: UP
    

Type Inference

TypeScript can infer types even if you don’t explicitly declare them.

let a = 5;
let b = 'app';
let c = true;

Union Types

Variables that can hold multiple types of values:

function fun(variable: string | number) {
    if (typeof variable === 'string') {
        return variable.toLowerCase();
    } else {
        return variable;
    }
}

console.log(fun('Apple'));  // Output: apple
console.log(fun(12));       // Output: 12

Intersection Types

Combine multiple types into one using the intersection (&) operator.

type Name = {
    name: string;
};

type Age = {
    age: number;
};

type User = Name & Age;

let user: User = {
    name: 'John',
    age: 30
};

console.log(user);

Type Aliases

You can create custom types for primitives or complex objects:

type Name = string;

type User = {
    firstname: string;
    lastname: string;
    age: number;
};

Interfaces

Interfaces are similar to type aliases but are more flexible. They define the shape of an object.

interface User {
    name: string;
    age: number;
}

function getUser(user: User) {
    console.log(user.name);
}

getUser({ name: 'Himanshu', age: 21 });

Classes in TypeScript

TypeScript supports object-oriented programming with the use of classes, making it easier to structure and manage code, especially when dealing with complex applications.

Defining a Class

A class in TypeScript is defined using the class keyword. It can have properties and methods.

class User {
    name: string;
    age: number;

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

    display(): void {
        console.log(`Name: ${this.name}, Age: ${this.age}`);
    }
}

const user = new User('Himanshu', 21);
user.display();  // Output: Name: Himanshu, Age: 21

Access Modifiers

TypeScript allows you to control the visibility of class properties and methods using access modifiers:

  • Public (default): Accessible from anywhere.

  • Private: Only accessible within the class itself.

  • Protected: Accessible within the class and its subclasses.

class Employee {
    public name: string;     // Accessible anywhere
    private salary: number;  // Accessible only within the class

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

    public getSalary(): number {
        return this.salary;
    }

    private calculateBonus(): number {
        return this.salary * 0.1;
    }
}

const employee = new Employee('John', 50000);
console.log(employee.name);       // Output: John
console.log(employee.getSalary()); // Output: 50000

Read-Only Properties

Properties can be marked as readonly, meaning they can only be initialized once, either directly or in the constructor.

class Car {
    readonly brand: string;

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

const myCar = new Car('Tesla');
console.log(myCar.brand);  // Output: Tesla
// myCar.brand = 'BMW';  // Error: Cannot assign to 'brand' because it is a read-only property.

Inheritance

Classes in TypeScript can extend other classes, allowing for the reuse of code.

class Animal {
    name: string;

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

    makeSound(): void {
        console.log(`${this.name} is making a sound.`);
    }
}

class Dog extends Animal {
    constructor(name: string) {
        super(name);
    }

    bark(): void {
        console.log(`${this.name} is barking.`);
    }
}

const dog = new Dog('Buddy');
dog.makeSound();  // Output: Buddy is making a sound.
dog.bark();       // Output: Buddy is barking.

Abstract Classes

Abstract classes cannot be instantiated directly. They are designed to be extended by other classes, and they can contain both abstract methods (methods without a body) and concrete methods.

abstract class Shape {
    abstract area(): number; // Abstract method

    display(): void {
        console.log('This is a shape.');
    }
}

class Rectangle extends Shape {
    width: number;
    height: number;

    constructor(width: number, height: number) {
        super();
        this.width = width;
        this.height = height;
    }

    area(): number {
        return this.width * this.height;
    }
}

const rect = new Rectangle(10, 20);
rect.display();        // Output: This is a shape.
console.log(rect.area()); // Output: 200

Static Methods and Properties

Static members belong to the class itself rather than to instances of the class. They can be accessed without creating an object of the class.

class MathHelper {
    static pi: number = 3.14;

    static calculateArea(radius: number): number {
        return this.pi * radius * radius;
    }
}

console.log(MathHelper.pi);               // Output: 3.14
console.log(MathHelper.calculateArea(10)); // Output: 314

Getters and Setters

Getters and setters allow you to control the access to a class's properties, adding logic when a property is read or modified.

class Person {
    private _age: number;

    constructor(age: number) {
        this._age = age;
    }

    get age(): number {
        return this._age;
    }

    set age(value: number) {
        if (value > 0) {
            this._age = value;
        } else {
            console.log('Please enter a valid age.');
        }
    }
}

const person = new Person(25);
console.log(person.age);  // Output: 25
person.age = -5;          // Output: Please enter a valid age.

Functions

Functions are core to TypeScript, just as in JavaScript, but with enhanced capabilities.

  • Named Function:

      function fun(): string {
          return 'hey';
      }
    
  • Anonymous Function:

      function(): void {
          console.log('This is an anonymous function');
      }
    
  • Arrow Function:

      const fun = (): string => {
          return 'apple';
      }
    
  • Explicit Return Type: Define the return type explicitly:

      function fun(): string {
          return 'hey';
      }
    
  • Implicit Return Type: TypeScript can infer the return type automatically:

      function fun() {
          return 'hey';
      }
    
  • Optional Parameters: Optional parameters let you define variables that might not be provided:

      function user(name: string, age: number, gender?: string): void {
          console.log(name + age + gender);
      }
    
      user('Himanshu', 21);
    
  • Default Parameters: You can assign default values to parameters:

      function user(name: string, age: number, gender: string = 'male'): void {
          console.log(name + age + gender);
      }
    
      user('Himanshu', 21);
    
  • REST Parameters: Handle multiple parameters with ... (spread):

      function names(...name: string[]): void {
          console.log(name.length);
      }
    
      names('one', 'two', 'three');
    

Modules

Modules in TypeScript allow you to organize your code into reusable blocks:

  • Export: Export variables or functions to use in other files:

      export const name = 'Himanshu';
    
  • Import: Import exported code into another file:

      import { name } from './utils';
    
      console.log(name);
    

Type Assertion

Type assertions let you tell TypeScript the exact type of a variable.

function fun() {
    const element = document.querySelector('p') as HTMLParagraphElement;
    console.log(element);
}

Literal Types

Literal types are specific values you can assign to a variable. They allow you to specify exact values rather than just broad types like string or number.

  • String, Number, Boolean Literal Types:

      let direction: 'up' | 'down';
      direction = 'up'; // This is valid
      direction = 'left'; // Error: "left" is not assignable
    

Conclusion

By now, you should have a solid understanding of how TypeScript works and how it enhances JavaScript development. From catching errors early to making your code more predictable and readable, TypeScript ensures that you write cleaner, safer, and more scalable applications.

Start implementing TypeScript in your next project and experience the benefits firsthand. With this powerful tool in your arsenal, you’ll become a more efficient and confident developer.