TypeScript Core Concepts

Variables and Types

In TypeScript, you can declare variables using let and const keywords, similar to modern JavaScript. TypeScript extends JavaScript’s variable types. You assign types to variables using the : syntax.

let username: string = 'John Doe';
let age: number = 30;
let isSubscriber: boolean = true;

If you try to assign a value of a different type to a variable, TypeScript will throw an error.

Understanding Basic Types

TypeScript offers several basic types that cover the majority of use cases:

  • Number: Represents numeric values.

    let age: number = 25;
    
  • String: Represents sequence of characters.

    let name: string = 'John';
    
  • Boolean: Represents true or false.

    let isHappy: boolean = true;
    
  • Any: Represents any type, a way to opt-out of type-checking.

    let item: any = { id: 1, name: 'Item' };
    

Using Advanced Types

Beyond basic types, TypeScript offers advanced types to handle more complex scenarios:

  • Enum: A way to give friendly names to sets of numeric values.
  • Tuple: Allows expressing an array where the type of a fixed number of elements is known.
  • Union: Allows a value to be one of several types.
  • Interface: Defines a contract for the structure of an object.
  • Null and Undefined: Represents the absence of a value or a variable that hasn't been assigned a value yet.
  • Void: Represents the absence of any value, typically used as the return type of functions that do not return a value.

Type Annotations and Inference

TypeScript uses type annotations to explicitly specify the type of a variable, and type inference when the type is implied by the value it is assigned. When you don’t specify a type, TypeScript will infer it based on the initial value.

let age = 30;  // TypeScript infers the type as number
let name: string = 'John'; // Explicit type annotation as string

Defining Functions

In TypeScript, you can specify the types of parameters and the return type of a function, enhancing reliability and readability:

function greet(name: string): string {
  return `Hello, ${name}!`;
}

In this function, name is expected to be a string, and the function is expected to return a string.

Optional and Default Parameters

TypeScript allows function parameters to be optional by appending a ? to the parameter name. You can also assign default values to parameters, which will be used if no value is provided for that parameter.

function greet(name: string = 'Guest', age?: number): string {
  return `Hello, ${name}! ${age ? `I see you're ${age}.` : ''}`;
}

Object-Oriented Programming

Interfaces

Interfaces in TypeScript are used to define the structure of an object, acting like a contract that specifies the properties and methods that a class must have.

interface Person {
  name: string;
  age: number;
  greet(): void;
}

Classes

Classes in TypeScript are blueprints for creating objects. They can implement interfaces to ensure they adhere to a certain structure.

class User implements Person {
  constructor(public name: string, public age: number) {}

  greet() {
    console.log(`Hi, I am ${this.name}, ${this.age} years old`);
  }
}

Access Modifiers

Access modifiers control the accessibility of the members of a class. The three access modifiers in TypeScript are:

  • Public: Accessible from any location.
  • Private: Accessible only within the declaring class.
  • Protected: Accessible within the declaring class and its subclasses.
class User {
  private password: string;

  constructor(public username: string, password: string) {
    this.password = password;
  }
}

Inheritance

Inheritance is a fundamental concept of Object-Oriented Programming that allows one class (subclass or derived class) to inherit properties and methods from another class (base or super class). Inheritance allows for code reusability and can establish a relationship between the parent and the child class.

Here is a simple example in TypeScript, where the Dog class inherits from the Animal class:

class Animal {
  name: string;

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

  move(distance: number = 0) {
    console.log(`${this.name} moved ${distance} meters.`);
  }
}

class Dog extends Animal {
  bark() {
    console.log('Woof! Woof!');
  }
}

const dog = new Dog('Buddy');
dog.bark(); // Output: Woof! Woof!
dog.move(10); // Output: Buddy moved 10 meters.

Polymorphism

Polymorphism is another core principle of Object-Oriented Programming in TypeScript, allowing methods to do different things based on the object it is acting upon. In simple words, polymorphism allows one interface to be used for different data types.

Here is an example of polymorphism in TypeScript, where the makeSound method behaves differently for instances of the Dog and Cat classes, even though they share the same interface:

interface Animal {
  makeSound(): void;
}

class Dog implements Animal {
  makeSound() {
    console.log('Woof! Woof!');
  }
}

class Cat implements Animal {
  makeSound() {
    console.log('Meow! Meow!');
  }
}

const myAnimal: Animal = new Dog();
myAnimal.makeSound(); // Output: Woof! Woof!

const anotherAnimal: Animal = new Cat();
anotherAnimal.makeSound(); // Output: Meow! Meow!

In this example, both Dog and Cat classes implement the Animal interface but provide different implementations for the makeSound method, demonstrating polymorphism.