TypeScript
1. What is TypeScript?
TypeScript = JavaScript + Types
Developed by Microsoft
It is a superset of JavaScript
Runs after compilation → converted to JavaScript
👉 Reality check:
JavaScript says: “Do whatever you want 😌”
TypeScript says: “Try that again… with rules 😐”
Why TypeScript?
Catch errors at compile time
Improves code readability
Better for large-scale applications
Strong support for IDE (VS Code)
2. Installation & Setup
Install TypeScript
npm install -g typescript
Check version
tsc -v
Compile file
tsc [Link]
3. Basic Types in TypeScript
Primitive Types
let name: string = "Aqib";
let age: number = 22;
let isStudent: boolean = true;
Special Types
let data: any = "anything"; // avoid using
let value: unknown;
let nothing: void;
👉 Irony moment:
any = “I installed TypeScript just to ignore it.”
4. Arrays & Tuples
Array
What is an Array?
An array is a collection of multiple values of the same type.
let numbers: number[] = [1, 2, 3];
👉 Meaning:
number[] → this array will only store numbers
[1, 2, 3] → valid
["a", 1] ❌ → error
Alternative Syntax
let numbers: Array<number> = [1, 2, 3];
👉 Both are same:
number[]
Array<number>
Example with Strings
let names: string[] = ["Aqib", "Rahul", "Ali"];
Mixed Types? (Not allowed directly)
let data: number[] = [1, "hello"]; ❌
👉 To allow multiple types:
let data: (number | string)[] = [1, "hello"];
Dynamic Nature of Arrays
let arr: number[] = [1, 2, 3];
[Link](4); // ✅ allowed
[Link](5); // ✅ allowed
👉 Arrays are flexible (dynamic size)
Key Idea
Same type
Any length
Order doesn’t matter for type checking
Tuples
What is a Tuple?
A tuple is a fixed-length array with fixed types at fixed positions
let user: [string, number] = ["Aqib", 22];
👉 Meaning:
Position Type Value
0 string "Aqib"
1 number 22
Important Rule
Order matters!
let user: [string, number] = [22, "Aqib"]; ❌
Fixed Length
let user: [string, number] = ["Aqib", 22];
[Link](30); ❌ (logically wrong, TS may allow but avoid)
👉 Tuple is meant to be fixed structure
Real Example
let product: [string, number, boolean] = ["Laptop", 50000, true];
👉 Represents:
Name
Price
Availability
Optional Tuple Values
let user: [string, number?] = ["Aqib"];
Named Tuples (Readable)
let user: [name: string, age: number] = ["Aqib", 22];
👉 Improves readability
Array vs Tuple
Feature Array Tuple
Type Same type Different types allowed
Length Dynamic Fixed
Order Not important Very important
Use case List of items Structured data
5. Objects
An object is a collection of key-value pairs, where each key has a specific type.
let user: { name: string; age: number } = {
name: "Aqib",
age: 22
};
Breakdown of the Syntax
let user: { name: string; age: number }
👉 This means:
user is an object
It must have:
o name → string
o age → number
{
name: "Aqib",
age: 22
}
👉 This is the actual value assigned
Type Safety
❌ Missing Property
let user: { name: string; age: number } = {
name: "Aqib"
};
👉 Error: age is missing
❌ Extra Property
let user: { name: string; age: number } = {
name: "Aqib",
age: 22,
city: "Mumbai"
};
👉 Error: city not allowed
❌ Wrong Type
let user: { name: string; age: number } = {
name: "Aqib",
age: "22"
};
👉 Error: age must be number
Key Concept
👉 TypeScript enforces:
Structure
Property names
Property types
This is called structural typing
Optional Properties
let user: { name: string; age?: number } = {
name: "Aqib"
};
👉 age? means optional
Readonly Properties
let user: { readonly id: number; name: string } = {
id: 1,
name: "Aqib"
};
[Link] = 2; ❌ Not allowed
Nested Objects
let user: {
name: string;
address: { city: string; pincode: number };
} = {
name: "Aqib",
address: {
city: "Mumbai",
pincode: 400001
}
};
👉 Objects inside objects = very common in real apps
Functions Inside Objects
let user: {
name: string;
greet: () => string;
} = {
name: "Aqib",
greet: () => "Hello"
};
Reusability Problem
If you write this again and again:
let user1: { name: string; age: number };
let user2: { name: string; age: number };
👉 ❌ Bad practice
Solution: Type Alias
type User = {
name: string;
age: number;
};
let user1: User = { name: "Aqib", age: 22 };
let user2: User = { name: "Ali", age: 25 };
Or Use Interface
interface User {
name: string;
age: number;
}
👉 Same use as type, but better for large apps
Inline Type vs Type Alias
Feature Inline Object Type Type Alias / Interface
Reusability ❌ No ✅ Yes
Readability ❌ Low ✅ High
Best Practice ❌ Avoid ✅ Use this
7. Union & Intersection
Union (OR)
let id: string | number;
Intersection (AND)
type A = { name: string };
type B = { age: number };
type C = A & B;
8. Functions in TypeScript
function add(a: number, b: number): number {
return a + b;
}
Optional Parameter
function greet(name?: string) {}
Default Parameter
function greet(name: string = "Guest") {}
9. Type Inference
Type Inference means:
👉 TypeScript automatically detects the type of a variable without you explicitly writing
it
let x = 10;
👉 TypeScript internally treats it as:
let x: number = 10;
How TypeScript Infers Types
TypeScript looks at:
Initial value
Context
Usage
1. Basic Type Inference
let name = "Aqib"; // string
let age = 22; // number
let isAdmin = false; // boolean
👉 No need to write types manually
2. Reassignment Behavior
let x = 10;
x = 20; // ✅ allowed
x = "hello"; ❌ error
👉 Once inferred → type is fixed
3. Inference in Functions
function add(a: number, b: number) {
return a + b;
}
👉 Return type inferred automatically as number
Equivalent to:
function add(a: number, b: number): number
4. Contextual Typing (Very Important)
[Link]("click", (event) => {
[Link]([Link]);
});
👉 TypeScript infers event type automatically based on context
5. Array Inference
let arr = [1, 2, 3];
👉 inferred as:
number[]
Mixed Values
let arr = [1, "hello"];
👉 inferred as:
(string | number)[]
6. Object Inference
let user = {
name: "Aqib",
age: 22
};
👉 inferred as:
{
name: string;
age: number;
}
⚠️ Where Type Inference Fails (Important)
This is the “until it doesn’t” part 👇
1. Undefined Initialization
let x;
👉 Type becomes:
any ❌
👉 Now anything is allowed:
x = 10;
x = "hello";
x = true;
🚨 Dangerous → no type safety
2. Complex Objects
let user = {
name: "Aqib"
};
Later:
[Link] = 22; ❌ error
👉 Because TypeScript inferred:
{ name: string }
👉 It doesn’t know about age
3. Function Parameters
function greet(name) {
return "Hello " + name;
}
👉 name becomes any ❌
👉 Fix:
function greet(name: string) {}
4. Empty Arrays
let arr = [];
👉 inferred as:
any[] ❌
11. Classes in TypeScript
A class is a blueprint to create objects.
👉 Think:
Class = Design 🧾
Object = Real item 🧾
Your Example Explained
class Person {
name: string;
constructor(name: string) {
[Link] = name;
}
greet() {
[Link]("Hello " + [Link]);
}
}
1. Class Declaration
class Person { }
👉 Creates a blueprint called Person
2. Property (Variable inside class)
name: string;
👉 This means:
Every Person object will have a name
It must be a string
3. Constructor (Very Important)
constructor(name: string) {
[Link] = name;
}
👉 What it does:
Runs automatically when object is created
Initializes values
this Keyword
👉 this refers to the current object
[Link] = name;
Means:
Assign input value to object's property
4. Method (Function inside class)
greet() {
[Link]("Hello " + [Link]);
}
👉 This is a behavior of the object
5. Creating Object (Instance)
const p1 = new Person("Aqib");
👉 What happens:
Constructor runs
name = "Aqib"
6. Calling Method
[Link]();
👉 Output:
Hello Aqib
Internal Flow (Important)
const p1 = new Person("Aqib");
👉 Behind the scenes:
1. New object created
2. Constructor called
3. [Link] = "Aqib"
Shortcut (Parameter Properties)
TypeScript gives a shortcut:
class Person {
constructor(public name: string) {}
greet() {
[Link]("Hello " + [Link]);
}
}
👉 No need to declare name separately
Access Modifiers (Very Important)
1. Public (default)
public name: string;
👉 Accessible everywhere
2. Private
private name: string;
👉 Only inside class
[Link] ❌ error
3. Protected
protected name: string;
👉 Accessible in class + child classes
Inheritance (Advanced but Important)
class Student extends Person {
grade: number;
constructor(name: string, grade: number) {
super(name);
[Link] = grade;
}
}
👉 extends → reuse parent class
Method Override
class Student extends Person {
greet() {
[Link]("Hi Student " + [Link]);
}
}
Readonly Property
class Person {
readonly id: number;
constructor(id: number) {
[Link] = id;
}
}
👉 Cannot change later
Common Mistakes
❌ Forgetting this
name = name; ❌ wrong
✔️ Correct:
[Link] = name;
❌ Not initializing properties
name: string; // error if not assigned
👉 Fix:
Use constructor
OR use !
name!: string;
Real-Life Example
class Car {
constructor(public brand: string, public price: number) {}
details() {
return `${[Link]} costs ${[Link]}`;
}
}
const car1 = new Car("BMW", 5000000);
[Link]([Link]());
Class vs Object Literal
Object
let user = {
name: "Aqib"
};
Class
class User {
constructor(public name: string) {}
}
👉 Use class when:
Multiple objects needed
Logic + behavior required
12. Access Modifiers
Access modifiers control who can access properties and methods of a class.
👉 In simple terms:
“Who is allowed to use this data?”
Basic Example
class User {
public name: string;
private age: number;
protected id: number;
constructor(name: string, age: number, id: number) {
[Link] = name;
[Link] = age;
[Link] = id;
}
}
1. public (Default)
public name: string;
👉 Accessible:
Inside class ✅
Outside class ✅
Child class ✅
Example
const user = new User("Aqib", 22, 101);
[Link]([Link]); // ✅ allowed
👉 If you don’t write anything → it is public by default
2. private (Most Restricted)
private age: number;
👉 Accessible:
Inside class ✅
Outside class ❌
Child class ❌
Example
const user = new User("Aqib", 22, 101);
[Link]([Link]); ❌ error
Access inside class
class User {
private age: number;
constructor(age: number) {
[Link] = age;
}
getAge() {
return [Link]; // ✅ allowed
}
}
👉 Use methods to expose private data safely
3. protected (Middle Level)
protected id: number;
👉 Accessible:
Inside class ✅
Outside class ❌
Child class ✅
Example with Inheritance
class User {
protected id: number;
constructor(id: number) {
[Link] = id;
}
}
class Admin extends User {
getId() {
return [Link]; // ✅ allowed (child class)
}
}
const admin = new Admin(101);
[Link]([Link]); ❌ error
Comparison Table
Modifier Inside Class Outside Class Child Class
public ✅ ✅ ✅
private ✅ ❌ ❌
protected ✅ ❌ ✅
👉 Real meaning:
public → everyone invited
private → VIP access only
protected → family members only
13. Generics
👉 Generics allow you to write flexible and reusable code
👉 Instead of fixing a type, you use a placeholder type
Example
function identity<T>(value: T): T {
return value;
}
Breakdown
<T> → Generic Type Parameter
👉 T is just a name (you can use anything like T, U, K)
👉 It means:
“I don’t know the type yet, but I’ll use it consistently”
value: T
👉 The function accepts a value of type T
: T
👉 Function returns the same type T
Usage
identity<string>("Hello"); // T = string
identity<number>(10); // T = number
👉 Output type matches input type
Why Not Use any?
❌ Using any
function identity(value: any): any {
return value;
}
👉 Problem:
No type safety
Type lost
✅ Using Generics
function identity<T>(value: T): T {
return value;
}
👉 Benefit:
Keeps type information
Safer code
Type Inference with Generics
You don’t always need to pass type manually:
identity("Hello"); // T automatically = string
identity(100); // T = number
👉 TypeScript is smart enough
Generics with Arrays
function getFirst<T>(arr: T[]): T {
return arr[0];
}
Example
getFirst([1, 2, 3]); // number
getFirst(["a", "b"]); // string
Generics with Objects
function printName<T>(obj: T): T {
return obj;
}
👉 But this is not useful unless we restrict it 👇
Generic Constraints
function printName<T extends { name: string }>(obj: T): T {
[Link]([Link]);
return obj;
}
Example
printName({ name: "Aqib", age: 22 }); // ✅
printName({ age: 22 }); ❌
👉 Now object must have name
Multiple Generics
function pair<T, U>(a: T, b: U): [T, U] {
return [a, b];
}
Example
pair("Aqib", 22); // [string, number]
Generics in Interfaces
interface Box<T> {
value: T;
}
Example
const box1: Box<string> = { value: "Hello" };
const box2: Box<number> = { value: 100 };
Generics in Classes
class DataStore<T> {
data: T[] = [];
add(item: T) {
[Link](item);
}
}
Example
const store = new DataStore<number>();
[Link](10);
[Link](20);
Generics vs Any
Feature Generics any
Type Safety ✅ Yes ❌ No
Flexibility ✅ Yes ✅ Yes
Type Retention ✅ Maintained ❌ Lost
14. Enums
Enum (short for enumeration) is used to define a set of named constants
enum Role {
ADMIN,
USER,
GUEST
}
👉 Instead of writing random values like "admin", "user", you use:
[Link]
[Link]
[Link]
What happens internally?
By default, TypeScript assigns numeric values:
enum Role {
ADMIN, // 0
USER, // 1
GUEST // 2
}
👉 So internally:
[Link] === 0
[Link] === 1
[Link] === 2
Usage Example
let userRole: Role = [Link];
if (userRole === [Link]) {
[Link]("Admin access");
}
Why Use Enums?
Without enum ❌
let role = "admin";
👉 Problem:
Typo risk ("admn")
No autocomplete
No strict checking
With enum ✅
let role: Role = [Link];
👉 Benefits:
Type safety
Autocomplete
Cleaner code
Types of Enums
1⃣ Numeric Enum (Default)
enum Status {
PENDING,
SUCCESS,
FAILED
}
👉 Values:
PENDING → 0
SUCCESS → 1
FAILED → 2
Custom Start Value
enum Status {
PENDING = 1,
SUCCESS,
FAILED
}
👉 Now:
PENDING → 1
SUCCESS → 2
FAILED → 3
2⃣ String Enum (Very Common in Real
Apps)
enum Role {
ADMIN = "admin",
USER = "user",
GUEST = "guest"
}
👉 Best for:
APIs
Backend communication
Example
let role: Role = [Link];
[Link](role); // "admin"
Numeric vs String Enum
Feature Numeric Enum String Enum
Default ✅ Yes ❌ No
Readability ❌ Low ✅ High
Debugging ❌ Hard ✅ Easy
Real-world use ⚠️ Less ✅ More
Reverse Mapping (Only Numeric)
enum Role {
ADMIN,
USER
}
[Link](Role[0]); // "ADMIN"
👉 Works only for numeric enums
Enums in Functions
function checkRole(role: Role) {
if (role === [Link]) {
[Link]("Full Access");
}
}
Enums in Objects
enum Status {
SUCCESS = "success",
ERROR = "error"
}
let response = {
status: [Link]
};
Common Mistakes
❌ Mixing types
enum Test {
A = "a",
B = 1 ❌ avoid mixing
}
❌ Using enums when not needed
👉 Sometimes union types are better:
type Role = "admin" | "user" | "guest";
Enum vs Union Type (Important)
Enum
enum Role {
ADMIN = "admin"
}
Union Type
type Role = "admin" | "user" | "guest";
When to use what?
✅ Use Enum:
When values are reused
When constants needed globally
✅ Use Union:
Simpler cases
Lightweight
15. Modules (Import/Export)
export const name = "Aqib";
import { name } from "./file";
16. [Link]
Used to configure TypeScript behavior
{
"compilerOptions": {
"target": "ES6",
"strict": true
}
}
17. TypeScript with React
Used for type safety in components
Helps with:
o Props validation
o State typing
type Props = {
name: string;
};
const Comp = ({ name }: Props) => {
return <h1>{name}</h1>;
};
18. Common Mistakes
Overusing any
Not enabling strict mode
Ignoring type errors
👉 Basically using TypeScript like JavaScript with extra steps