Skip to main content

Command Palette

Search for a command to run...

Everything You Need to Know About TypeScript

Published
β€’12 min read
Everything You Need to Know About TypeScript
A

I am a Full Stack Developer and an AI enthusiast!

So What is TypeScript?πŸ€”

TypeScript is a programming language that extends JavaScript. It can also be said that TypeScript is a superset of JavaScript, which has several useful features, such as type definitions. TypeScript is created and managed by Microsoft.

Why TypeScript?

You might wonder why we should use Typescript over Javascript. To answer this question, we need to understand Javascript and its limitations. JavaScript is a loosely typed language, meaning variables can change types during runtime. This might seem like an interesting feature to most beginners, but it is not. It can lead to several bugs, which no one wants.

For example, let’s take a look at this JS code

const sum = (a,b) => a+b

sum(2,3) //=> 5
sum(2,'3') //=> 23

Here’s a simple function that takes two parameters and returns their sum. Notice how it works perfectly fine when passing two numbers or integers as arguments, but when we pass a string instead of a number, it concatenates the two arguments and returns. But we don’t want this to happen; ideally, it should throw an error message like β€œOnly number types are allowed.”

So How can we fix this by using TS? let’s see.

const sum = (a:number, b:number) => a+b

sum(2,3) //=> 5
sum(2,'3') // Argument of type 'string' is not assignable to parameter of type 'number'.
       ~~

Now, we have assigned a type to function params that only accept number values. If we try to send a non-number value, it will show us an error saying that it only accepts number values. Because of these, we have managed to catch potential bugs.


All The Types in Typescript 🎯

Basic/Primitive Types

TypeDescriptionExample
NumberRepresents numeric values, including integers and floating points.let age: number = 23;
StringRepresents text values. (Anything within quotes)let name: string = "John";
BooleanRepresents true or false.let isActive: boolean = true;
NullRepresents an empty valuelet data: null = null;
UndefinedRepresents an uninitialized variable.let salary: undefined;
BigIntSame as numbers but also supports large numbers.let large: bigint = 9007199254740991n;
SymbolReprest unique values. (can be used as keys)let uniqueKey: symbol = Symbol("key");

Object Types

TypeDescriptionExample
ObjectData structure having key-value pair.let user: object = { id: 1, name: "John" };
ArrayRepresents a collection of elements of similar type.let numbers: number[] = [1, 2, 3];
TupleFixed-size array with different types at specific positions.let person: [string, number] = ["John", 23];

Special Type

TypeDescriptionExample
anyBy using any type it disables type checkinglet data: any = "Hello"; data = 10;
unknownSimilar to any but requires type-checking before usage.let value: unknown = "Test"; if (typeof value === "string") console.log(value.toUpperCase());
neverRepresents values that never occur (e.g., errors, infinite loops).function throwError(): never { throw new Error("Error!"); }
voidRepresents functions that don’t return anything.function logMessage(): void { console.log("Hello!"); }

Function Types

TypeDescriptionExample
FunctionGeneral function type.let greet: Function = () => "Hello";
Function SignatureDefines the structure of a function.let add: (a: number, b: number) => number = (a, b) => a + b;

Advanced Types

TypeDescriptionExample
UnionWhen we want a variable to have multiple types, we can achieve that by using a union type.let value : number
intersectionCombines multiple types.type A = { name: string }; type B = { age: number }; type Person = A & B;
literalRestricts values to specific literals.`let status: "success"
type aliasCustom type definitions.type User = { id: number; name: string }; let user: User = { id: 1, name: "John" };
interfaceSimilar to type alias but extendable.interface User { id: number; name: string; }

type VS interface πŸ€”

1. Syntax & Basic Usage

βœ… interface (Used for Object Structures)

  • Used mainly to define the shape of an object.

  • Supports declaration merging (can be extended later).

  • Can be implemented by classes.

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

const user: User = { id: 1, name: "Akash" };

βœ… type (More Flexible, Works with Primitives, Unions, Tuples, etc.)

  • Can be used for primitives, unions, intersections, and objects.

  • Cannot be re-declared (no merging).

  • More powerful when dealing with complex types.

type User = { id: number; name: string };

const user: User = { id: 1, name: "Akash" };

2. Extending & Merging

βœ… Extending in interface (Built-in Feature)

Interfaces can be extended using the extends keyword.

interface Person {
  name: string;
}

interface Employee extends Person {
  id: number;
}

const emp: Employee = { id: 101, name: "Akash" };

βœ… Extending in type (Using Intersection &)

Types don't support extends but use intersections (&) instead.

type Person = { name: string };
type Employee = Person & { id: number };

const emp: Employee = { id: 101, name: "Akash" };

3. Declaration Merging

βœ… Interface Supports Declaration Merging

If you declare the same interface twice, TypeScript merges them.

interface User {
  id: number;
}

interface User {
  name: string;
}

const user: User = { id: 1, name: "Akash" }; // βœ… Works (merged)

❌ Type Does NOT Support Declaration Merging

If you try to declare the same type twice, you get an error.

type User = { id: number };
type User = { name: string }; // ❌ Error: Duplicate identifier 'User'

4. Using in Classes

βœ… Interface Works with implements in Classes

Interfaces are designed for OOP-style inheritance and can be implemented by classes.

interface Animal {
  name: string;
  speak(): void;
}

class Dog implements Animal {
  name = "Dog";
  speak() {
    console.log("Woof!");
  }
}

❌ Type Cannot Be Implemented in Classes

Types cannot be used with implements.

type Animal = { name: string; speak(): void };

class Dog implements Animal {  // ❌ Error: 'Animal' only refers to a type but is being used as a value
  name = "Dog";
  speak() {
    console.log("Woof!");
  }
}

5. Using with Functions & Unions

βœ… Type Supports Unions (Interfaces Cannot)

type allows defining unions, while interface does not.

type ID = number | string;  // βœ… Allowed
let userId: ID = 101;
userId = "user123";
interface ID = number | string; // ❌ Error: Interface cannot define a union type

βœ… Both Can Be Used for Function Types

Both type and interface can define function signatures.

// Using interface
interface AddFn {
  (a: number, b: number): number;
}

const add: AddFn = (a, b) => a + b;

// Using type
type AddFn = (a: number, b: number) => number;

const sum: AddFn = (a, b) => a + b;

When to Use interface vs type?

Featureinterface βœ…type βœ…
Defining Object Shapeβœ… Preferredβœ… Works
Extending Another Typeβœ… extendsβœ… & (Intersection)
Declaration Mergingβœ… Yes (Merges)❌ No (Throws Error)
Works with Classesβœ… Yes (implements)❌ No
Supports Union Types❌ Noβœ… Yes
Function Typesβœ… Yesβœ… Yes
Works with Primitives❌ Noβœ… Yes (type Age = number;)

Some TipsπŸš€

βœ” Use interface when defining object structures, especially if they need to be extended.
βœ” Use type for more advanced cases like unions, tuples, function types, or when combining multiple types.
βœ” If working with classes, prefer interface since it works with implements.
βœ” If needing to merge declarations (e.g., third-party libraries), use interface.


Type Assertions

In TypeScript, type assertions and type casting are ways to tell the compiler that you know more about a value’s type than it does.

Syntax

There are two ways to use type assertions:

  1. "as" Syntax (Preferred)

     let value: unknown = "Hello, TypeScript";
     let strLength: number = (value as string).length;
    
  2. Angle Bracket Syntax (Not recommended with JSX)

     let value: unknown = "Hello, TypeScript";
     let strLength: number = (<string>value).length;
    

Use Case

  • When working with unknown or any types.

  • When dealing with DOM elements in TypeScript.

  • When narrowing down types in complex scenarios.

Example: Accessing DOM Elements

const inputElement = document.getElementById("myInput") as HTMLInputElement;
inputElement.value = "Hello!";

Class

Classes allow you to define reusable blueprints for creating objects (Instances of a class) while adding type safety to your code.

1. Basic Class

class Person {
   constructor(name: string, age: number, lang: string, color: string) {
    this.name = name;
    this.age = age;
    this.lang = lang;
    this.color = color;
  }

  greet(): void {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

const person1 = new Person("John", 23, 'English', 'Blue');
person1.greet();

2. Access Modifiers

TypeScript provides public, private, and protected

class Employee {
  constructor(
               public readonly name: string;  // public means Accessible everywhere, 
               // readonly is used to prevent modification after initialization.
               private salary: number; // Only accessible inside the class
               protected department: string; // Accessible in this class and subclasses
              ) {
    this.name = name;
    this.salary = salary;
    this.department = department;
  }

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

class Manager extends Employee {
  constructor(name: string, salary: number, department: string) {
    super(name, salary, department);
  }

const emp = new Employee("Alice", 50000, "IT");
console.log(emp.salary); // ❌ Error: Private property
console.log(emp.department); // // ❌ Error Protected property
emp.name = "Henry"; // ❌ Error: Cannot modify a readonly property
console.log(emp.getSalary()); // βœ… Allowed

const mgr = new Manager("Bob", 70000, "HR");
mgr.showDepartment(); // βœ… Works because `department` is protected

3. Interfaces with Classes

interface Musician {
    name: string,
    instrument: string,
    play(action: string):string
}

class Guitartist implements Musician{
    constructor(name: string,
    instrument: string,
    ){
        this.name = name;
        this.instrument = instrument;
    }
    play(action:string){
        return `${this.name} ${action} the ${this.instrument}`
    }
}    

const user = Guitarist('John','Guitar')
user.play('plays') // John plays the Guitar

4. Static Properties & Methods

Static members belong to the class, not an instance.

class MathUtils {
  static PI: number = 3.1416;

  static areaOfCircle(radius: number): number {
    return this.PI * radius * radius;
  }
}

console.log(MathUtils.PI); // 3.1416
console.log(MathUtils.areaOfCircle(5)); // 78.54

5. Getters & Setters

Encapsulate logic when accessing properties.

class BankAccount {
  private _balance: number = 0;

  get balance(): number {
    return this._balance;
  }

  set balance(amount: number) {
    if (amount < 0) {
      console.log("Balance cannot be negative!");
      return;
    }
    this._balance = amount;
  }
}

const account = new BankAccount();
account.balance = 1000; // βœ… Valid
console.log(account.balance); // 1000
account.balance = -500; // ❌ Balance cannot be negative!

6. Inheritance

A class can extend another class to reuse functionality.

class Vehicle {
  constructor(public brand: string) {}

  start(): void {
    console.log(`${this.brand} is starting...`);
  }
}

class Car extends Vehicle {
  constructor(brand: string, public model: string) {
    super(brand);
  }

  showModel(): void {
    console.log(`This is a ${this.model}`);
  }
}

const myCar = new Car("Toyota", "Corolla");
myCar.start(); // "Toyota is starting..."
myCar.showModel(); // "This is a Corolla"

Index Signature in TypeScript

Index signatures allow you to define dynamic property names in an object type. This is useful when you don't know all the property names in advance.

  1. Basic Syntax

type MyObject = {
  [key: string]: number; // Any property with a string key must have a number value
};

const obj: MyObject = {
  apples: 10,
  bananas: 20,
  oranges: 30,
};

console.log(obj["apples"]); // βœ… 10
console.log(obj["grapes"]); // βœ… Undefined (but no TypeScript error)

2. Readonly Index Signature

If you want to prevent modifications after initialization, use readonly:

type Config = {
  readonly [key: string]: string;
};

const settings: Config = {
  theme: "dark",
  language: "en",
};

// settings.theme = "light"; // ❌ Error: Cannot assign to 'theme' because it is a read-only property

type NumberMap = {
  [index: number]: string; // Number keys, but treated as strings internally
};

const items: NumberMap = {
  1: "One",
  2: "Two",
};

console.log(items[1]); // βœ… "One"
console.log(items["1"]); // βœ… "One" (Converted to string)

3. Restricting Index Signature to Specific Keys

You can allow both dynamic keys and specific properties:

type User = {
  id: number;
  name: string;
  color?: string; // Optional
  [key: string]: string | number; // Allows additional properties
};

const user: User = {
  id: 1,
  name: "Jhon",
  email: "johnh@example.com", // Allowed due to index signature
  phone: "1234567890",
};
  • The id and name properties are required.

  • Other properties are allowed as long as they are strings or numbers.


keyof Type Assertion

The keyof operator lets you create a type based on an object's keys.

Basic Example

type Person = {
  name: string;
  age: number;
};

type PersonKeys = keyof Person; // "name" | "age"

let key: PersonKeys = "name"; // βœ… Allowed
// key = "email"; // ❌ Error: "email" is not a key of Person
  • keyof Person creates a union type "name" | "age".

  • You can use it to restrict valid property keys.


Generics in TypeScript

Generics allow you to create reusable, type-safe components, functions, and classes that work with multiple types instead of a single one.

1. Basic Generics in Functions

Instead of using any, generics help maintain type safety.

Without Generics (using any)

function identity(value: any): any {
  return value;
}

let result = identity(10); // ❌ No type safety, `result` is `any`

With Generics (T)

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

let numberResult = identity<number>(10);  // βœ… Type is number
let stringResult = identity<string>("Hello"); // βœ… Type is string
  • <T> represents a generic type variable.

  • T is inferred when calling the function.


Promise Types in TypeScript

A Promise in TypeScript represents an asynchronous operation that can resolve with a value or reject with an error. The type system helps ensure that the correct data types are handled properly.

1. Basic Promise Syntax

A Promise always resolves to a specific type.

const getData = (): Promise<string> => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("Hello, John!"), 1000);
  });
};

βœ… Promise<string> means the function returns a Promise that resolves to a string.


Conclusion 🎯

TypeScript is a powerful tool that enhances JavaScript by adding static types, making code more reliable and easier to maintain. It helps catch errors early, improves code readability, and supports advanced features like interfaces, generics, and type assertions. Whether you're working on small projects or large-scale applications, TypeScript ensures better structure and scalability. By using TypeScript, developers can write safer, more efficient code while still enjoying the flexibility of JavaScript. If you haven’t tried it yet, now is the perfect time to start!

More from this blog

Akash's blog

8 posts

A Full Stack Developer and AI enthusiast.