Everything You Need to Know About TypeScript

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
| Type | Description | Example |
| Number | Represents numeric values, including integers and floating points. | let age: number = 23; |
| String | Represents text values. (Anything within quotes) | let name: string = "John"; |
| Boolean | Represents true or false. | let isActive: boolean = true; |
| Null | Represents an empty value | let data: null = null; |
| Undefined | Represents an uninitialized variable. | let salary: undefined; |
| BigInt | Same as numbers but also supports large numbers. | let large: bigint = 9007199254740991n; |
| Symbol | Represt unique values. (can be used as keys) | let uniqueKey: symbol = Symbol("key"); |
Object Types
| Type | Description | Example |
| Object | Data structure having key-value pair. | let user: object = { id: 1, name: "John" }; |
| Array | Represents a collection of elements of similar type. | let numbers: number[] = [1, 2, 3]; |
| Tuple | Fixed-size array with different types at specific positions. | let person: [string, number] = ["John", 23]; |
Special Type
| Type | Description | Example |
| any | By using any type it disables type checking | let data: any = "Hello"; data = 10; |
| unknown | Similar to any but requires type-checking before usage. | let value: unknown = "Test"; if (typeof value === "string") console.log(value.toUpperCase()); |
| never | Represents values that never occur (e.g., errors, infinite loops). | function throwError(): never { throw new Error("Error!"); } |
| void | Represents functions that donβt return anything. | function logMessage(): void { console.log("Hello!"); } |
Function Types
| Type | Description | Example |
| Function | General function type. | let greet: Function = () => "Hello"; |
| Function Signature | Defines the structure of a function. | let add: (a: number, b: number) => number = (a, b) => a + b; |
Advanced Types
| Type | Description | Example |
| Union | When we want a variable to have multiple types, we can achieve that by using a union type. | let value : number |
| intersection | Combines multiple types. | type A = { name: string }; type B = { age: number }; type Person = A & B; |
| literal | Restricts values to specific literals. | `let status: "success" |
| type alias | Custom type definitions. | type User = { id: number; name: string }; let user: User = { id: 1, name: "John" }; |
| interface | Similar 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?
| Feature | interface β
| 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:
"as" Syntax (Preferred)
let value: unknown = "Hello, TypeScript"; let strLength: number = (value as string).length;Angle Bracket Syntax (Not recommended with JSX)
let value: unknown = "Hello, TypeScript"; let strLength: number = (<string>value).length;
Use Case
When working with
unknownoranytypes.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.
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
idandnameproperties 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 Personcreates 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.Tis 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!



