1. What is TypeScript?
You already know JavaScript. TypeScript is JavaScript with types added on top. Every valid JavaScript file is already valid TypeScript — TypeScript just gives you extra tools to catch mistakes before your code runs.
Who made it and why?
Microsoft created TypeScript in 2012. As JavaScript projects got bigger, developers kept running into bugs that only showed up when the code ran. TypeScript catches those bugs at compile time — before you even run the code.
How does it work?
Browsers don't understand TypeScript. So TypeScript compiles (transpiles) down to plain JavaScript using the tsc compiler.
// Your TypeScript file: app.ts let name: string = "Darshan"; // After compilation → app.js (plain JavaScript) var name = "Darshan";
The flow: .ts file → tsc compiler → .js file → browser runs the .js
Why use TypeScript?
| Benefit | What it means |
|---|---|
| Catches bugs early | Errors show up while you code, not when users hit them |
| Better IDE support | Autocomplete, hover info, refactoring — all powered by types |
| Self-documenting | Types tell other developers what a function expects and returns |
| Scales better | Large projects with many developers stay manageable |
function add(a, b) { return a + b; } add(5, "10"); // Returns "510" — no error, silent bug!TypeScript — bug caught at compile time:
function add(a: number, b: number): number { return a + b; } add(5, "10"); // ERROR: Argument of type 'string' is not assignable to parameter of type 'number'
2. Installation & Setup
You need Node.js installed first (which gives you npm). Then install TypeScript globally:
# Install TypeScript globally npm install -g typescript # Check version tsc --version # Compile a TypeScript file tsc app.ts // Creates app.js in the same folder # Compile and watch for changes tsc app.ts --watch
tsconfig.json — Project Configuration
For real projects, you create a tsconfig.json file that tells TypeScript how to compile your code. Generate one with tsc --init.
{
"compilerOptions": {
"target": "ES6", // Which JS version to compile to
"module": "commonjs", // Module system (commonjs, ES6, etc.)
"strict": true, // Enable all strict type checks
"outDir": "./dist", // Where compiled JS files go
"rootDir": "./src" // Where your TS source files are
}
}
strict mode forces you to declare types — no implicit any allowed.
3. Basic Types (Core Topic)
In JavaScript, variables can hold any type and change type anytime. TypeScript lets you lock a variable to a specific type using type annotations.
Syntax: Type Annotations
// JavaScript way — no types let name = "Darshan"; // TypeScript way — type annotation after the colon let name: string = "Darshan"; // ^^^^^^^^ this is the type annotation
All Basic Types
| Type | What it holds | Example |
|---|---|---|
string | Text | let city: string = "Mumbai"; |
number | Integers and decimals | let age: number = 22; |
boolean | true or false | let isActive: boolean = true; |
any | Anything (disables type checking) | let data: any = "hello"; |
void | No return value (for functions) | function log(): void { } |
null | Intentional absence of value | let x: null = null; |
undefined | Variable declared but not assigned | let y: undefined = undefined; |
never | Function that never returns | Throws error or infinite loop |
Examples for Each Type
// string let company: string = "TCS"; // number (integers AND decimals — no separate int/float) let score: number = 95; let price: number = 19.99; // boolean let isPassed: boolean = true; // any — opt out of type checking (avoid in real code) let random: any = 42; random = "now a string"; // No error — any allows anything random = true; // Still no error // void — function that doesn't return anything function sayHello(): void { console.log("Hello!"); // no return statement } // never — function that NEVER completes normally function throwError(msg: string): never { throw new Error(msg); // always throws, never returns }
Type Inference — TypeScript Guesses the Type
You don't always need to write the type. If you assign a value immediately, TypeScript infers the type automatically:
let city = "Mumbai"; // TypeScript infers: string let age = 22; // TypeScript infers: number let done = true; // TypeScript infers: boolean city = 100; // ERROR — city is already inferred as string
any defeats the purpose of TypeScript. If everything is any, you're just writing JavaScript with extra steps. Use it only when you genuinely don't know the type (like data from a third-party library).
void means the function returns nothing (it finishes, just no value). never means the function literally never finishes — it throws an error or loops forever. This distinction is a common exam question.
4. Arrays and Tuples
Typed Arrays
In JavaScript, an array can hold anything. In TypeScript, you can restrict what goes in:
// Two ways to declare typed arrays — both are identical let scores: number[] = [90, 85, 78]; let scores: Array<number> = [90, 85, 78]; // String array let names: string[] = ["Alice", "Bob"]; // Mixed? Use union type let mixed: (string | number)[] = ["hello", 42]; scores.push("A"); // ERROR — "A" is a string, not a number
Tuples — Fixed-Length, Fixed-Type Arrays
A tuple is an array where each position has a specific type. The length and order are fixed.
// Tuple: first element is string, second is number let person: [string, number] = ["Darshan", 22]; // Access like normal array console.log(person[0]); // "Darshan" (string) console.log(person[1]); // 22 (number) // ERROR — wrong order let wrong: [string, number] = [22, "Darshan"]; // Type mismatch! // ERROR — wrong length let bad: [string, number] = ["only one"]; // Missing second element
[string, number] is NOT the same as [number, string]. Exams love swapping the order to test you.
5. Enums
Enums let you define a set of named constants. Use them when you have a fixed group of related values (directions, colors, statuses).
Numeric Enums (default)
enum Direction { Up, // 0 Down, // 1 Left, // 2 Right // 3 } let move: Direction = Direction.Up; console.log(move); // 0 console.log(Direction.Right); // 3
By default, the first value is 0 and each subsequent value increments by 1. You can start from a different number:
enum Status { Active = 1, // 1 Inactive, // 2 (auto-increments) Pending // 3 }
String Enums
enum Color { Red = "RED", Green = "GREEN", Blue = "BLUE" } console.log(Color.Red); // "RED"
Direction.Left?" — count from 0. Up=0, Down=1, Left=2, Right=3. Unless a starting number is specified.
6. Functions with Types
In JavaScript, functions accept anything and return anything. TypeScript lets you specify parameter types and return types.
Basic Typed Function
// JavaScript function add(a, b) { return a + b; } // TypeScript — types for parameters AND return value function add(a: number, b: number): number { return a + b; }
Optional Parameters
Add ? after the parameter name to make it optional. Optional parameters must come after required ones.
function greet(name: string, greeting?: string): string { return (greeting || "Hello") + ", " + name; } greet("Darshan"); // "Hello, Darshan" greet("Darshan", "Hey"); // "Hey, Darshan"
Default Parameters
function greet(name: string = "World"): string { return "Hello, " + name; } greet(); // "Hello, World" greet("Darshan"); // "Hello, Darshan"
Arrow Functions with Types
const multiply = (a: number, b: number): number => a * b; const logMessage = (msg: string): void => { console.log(msg); };
name?:string means you can call the function without that argument. Default parameter name:string = "X" provides a fallback value. Both make the argument non-mandatory, but they work differently.
7. Interfaces (Very Important)
An interface defines the shape of an object — what properties it must have and their types. Think of it as a contract: any object claiming to be this type must have these properties.
Basic Interface
interface User { name: string; age: number; email: string; } // Object must match the shape exactly let user: User = { name: "Darshan", age: 22, email: "darshan@example.com" }; // ERROR — missing 'email' property let badUser: User = { name: "Test", age: 20 };
Optional Properties
interface User { name: string; age: number; email?: string; // optional — may or may not exist } let user: User = { name: "Darshan", age: 22 }; // OK — email is optional
Readonly Properties
interface User { readonly id: number; // cannot be changed after creation name: string; } let user: User = { id: 1, name: "Darshan" }; user.name = "New Name"; // OK user.id = 2; // ERROR — readonly!
Extending Interfaces
One interface can inherit from another using extends:
interface User { name: string; age: number; } interface Admin extends User { role: string; } // Admin must have ALL User properties + role let admin: Admin = { name: "Darshan", age: 22, role: "superadmin" };
Interface for Function Types
interface MathFunc { (a: number, b: number): number; } const add: MathFunc = (a, b) => a + b; const sub: MathFunc = (a, b) => a - b;
Interface vs Type Alias
| Feature | interface | type |
|---|---|---|
| Extend/inherit | extends | & (intersection) |
| Declaration merging | Yes (same name = merged) | No (error if duplicate) |
| Union types | Cannot do | type X = A | B |
| Primitives | Cannot represent | type ID = string |
| Best for | Object shapes, class contracts | Unions, aliases, complex types |
8. Type Aliases
The type keyword creates a custom name for any type — simple or complex.
Basic Type Alias
type StringOrNumber = string | number; let id: StringOrNumber = 101; // OK id = "ABC-101"; // Also OK id = true; // ERROR — boolean not allowed
Union Types — "this OR that"
type Status = "active" | "inactive" | "pending"; let userStatus: Status = "active"; // OK userStatus = "banned"; // ERROR — not in the union
Intersection Types — "this AND that"
type HasName = { name: string }; type HasAge = { age: number }; type Person = HasName & HasAge; // Must have BOTH name AND age let p: Person = { name: "Darshan", age: 22 }; // OK
Literal Types — Exact Values Only
type Direction = "up" | "down" | "left" | "right"; let dir: Direction = "up"; // OK dir = "diagonal"; // ERROR — not a valid direction
| for union, & for intersection.
9. Classes in TypeScript
TypeScript classes are JavaScript classes with access modifiers, type annotations, and shortcuts.
Access Modifiers
| Modifier | Accessible from | Default? |
|---|---|---|
public | Everywhere | Yes (if you don't write anything) |
private | Only inside the same class | No |
protected | Inside the class + child classes | No |
class Employee { public name: string; private salary: number; protected department: string; constructor(name: string, salary: number, dept: string) { this.name = name; this.salary = salary; this.department = dept; } getSalary(): number { return this.salary; // OK — accessing private inside the class } } let emp = new Employee("Darshan", 50000, "IT"); console.log(emp.name); // OK — public console.log(emp.salary); // ERROR — private, cannot access outside class console.log(emp.department); // ERROR — protected, only in class or subclass
Readonly Properties
class User { readonly id: number; name: string; constructor(id: number, name: string) { this.id = id; // OK — can set in constructor this.name = name; } } let u = new User(1, "Darshan"); u.id = 2; // ERROR — readonly, can only be set in constructor
Constructor Shorthand
TypeScript lets you declare and initialize properties directly in the constructor parameters:
// Long way class User { public name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } } // Shorthand — does EXACTLY the same thing class User { constructor(public name: string, private age: number) {} }
public, private, protected) or readonly before the parameter. Without a modifier, it's just a regular parameter — not a class property.
Implementing Interfaces
interface IUser { name: string; greet(): string; } class User implements IUser { constructor(public name: string) {} greet(): string { return "Hi, I'm " + this.name; } } // Class MUST have everything the interface requires
Abstract Classes
Abstract classes are base classes that cannot be instantiated directly. They can have abstract methods (no body — child must implement) and regular methods.
abstract class Shape { abstract area(): number; // no body — child MUST implement describe(): string { return "I am a shape with area: " + this.area(); } } class Circle extends Shape { constructor(private radius: number) { super(); } area(): number { return Math.PI * this.radius ** 2; } } // let s = new Shape(); // ERROR — cannot instantiate abstract class let c = new Circle(5); // OK
public = everywhere. protected = class + subclasses. private = class only. If nothing is written, it's public by default. This is asked repeatedly.
10. Generics (Basics)
Generics let you write functions, classes, and interfaces that work with any type while still being type-safe. Think of <T> as a placeholder for "whatever type you give me."
The Problem Generics Solve
// Without generics — works but loses type info function identity(arg: any): any { return arg; } let result = identity("hello"); // result is 'any', not 'string' // With generics — type-safe AND flexible function identity<T>(arg: T): T { return arg; } let result = identity<string>("hello"); // result is 'string' let num = identity<number>(42); // num is 'number'
Generic Interface
interface ApiResponse<T> { data: T; status: number; message: string; } let userResponse: ApiResponse<string> = { data: "Darshan", status: 200, message: "OK" }; let numResponse: ApiResponse<number[]> = { data: [1, 2, 3], status: 200, message: "OK" };
Generic Class
class Box<T> { private content: T; constructor(value: T) { this.content = value; } getValue(): T { return this.content; } } let stringBox = new Box<string>("Hello"); let numberBox = new Box<number>(100);
T is just a convention. You can use any letter: T (Type), K (Key), V (Value), E (Element). The angle brackets <T> declare the generic type parameter.
function identity<T>(arg: T): T, read it as: "This function takes something of type T and returns the same type T." If called with identity<number>(5), T becomes number, so it returns a number.
11. Type Guards and Narrowing
When a variable can be multiple types (union type), you need to narrow it down to a specific type before using type-specific operations.
typeof Guard
function print(value: string | number) { if (typeof value === "string") { // TypeScript knows value is a string here console.log(value.toUpperCase()); } else { // TypeScript knows value is a number here console.log(value.toFixed(2)); } }
instanceof Guard
class Dog { bark() { return "Woof!"; } } class Cat { meow() { return "Meow!"; } } function speak(pet: Dog | Cat) { if (pet instanceof Dog) { console.log(pet.bark()); } else { console.log(pet.meow()); } }
Type Assertions
When you know the type better than TypeScript does, use assertions to tell it:
// Two syntaxes — both do the same thing let value: any = "hello"; // Syntax 1: as keyword (preferred) let length: number = (value as string).length; // Syntax 2: angle bracket (cannot use in JSX/React) let length: number = (<string>value).length;
Non-null Assertion
let element = document.getElementById("app"); // type: HTMLElement | null // Adding ! tells TypeScript: "Trust me, this is NOT null" let el = document.getElementById("app")!; // type: HTMLElement
value as string but value is actually a number, you'll get a runtime error. Use them only when you're truly sure of the type.
12. Utility Types
TypeScript provides built-in utility types that transform existing types. You don't need to memorize all of them, but know the common ones:
| Utility Type | What it does | Example |
|---|---|---|
Partial<T> | Makes ALL properties optional | Partial<User> — can have some or no User fields |
Required<T> | Makes ALL properties required | Opposite of Partial |
Readonly<T> | Makes ALL properties readonly | Cannot modify any property |
Pick<T, K> | Selects only specified properties | Pick<User, "name" | "age"> |
Omit<T, K> | Removes specified properties | Omit<User, "password"> |
Record<K, V> | Creates object type with keys K and values V | Record<string, number> |
interface User { name: string; age: number; email: string; } // Partial — all properties become optional function updateUser(user: Partial<User>) { // Can pass { name: "X" } without age and email } // Pick — only selected properties type UserPreview = Pick<User, "name" | "age">; // UserPreview = { name: string; age: number; } (no email) // Omit — everything except specified type PublicUser = Omit<User, "email">; // PublicUser = { name: string; age: number; } (email removed) // Record — map of keys to values type Scores = Record<string, number>; let marks: Scores = { math: 90, science: 85 };
13. TypeScript vs JavaScript — Complete Comparison
| Feature | JavaScript | TypeScript |
|---|---|---|
| Type system | Dynamic (types checked at runtime) | Static (types checked at compile time) |
| File extension | .js | .ts |
| Type annotations | Not supported | let x: number = 5; |
| Interfaces | Not available | Fully supported |
| Enums | Not available | Fully supported |
| Generics | Not available | Fully supported |
| Access modifiers | Not available (ES2022 has #private) | public, private, protected |
| Compilation | Interpreted directly by browser | Must compile to JS first (tsc) |
| Error detection | Runtime only | Compile time + runtime |
| Tooling | Good | Excellent (autocomplete, refactoring) |
| Browser support | Native | Not native — compiles to JS |
| Learning curve | Lower | Higher (must learn types) |
| Superset? | — | Yes — all JS is valid TS |
| Created by | Brendan Eich (Netscape, 1995) | Microsoft (2012) |
| Used by Angular? | No (AngularJS used JS) | Yes — Angular requires TypeScript |
14. Practice Questions
enum Fruit { Apple, Banana, Cherry } console.log(Fruit.Cherry);
void is for functions that complete but return no value.interface Car { readonly brand: string; model: string; } let car: Car = { brand: "Toyota", model: "Camry" }; car.brand = "Honda";
readonly property is set, it cannot be changed.[string, number] — Tuples use square brackets with the types in order. This is different from a union array.? mean in this interface?
interface Product { name: string; price: number; description?: string; }
? after a property name makes it optional. The object can be created with or without it.public = everywhere, private = same class only, protected = same class + child classes.function identity<T>(arg: T): T { return arg; } let result = identity<number>(42);
<number>, the generic T becomes number. So the function takes a number and returns a number.number for all numeric values (both integers and decimals). There is no separate float or int type.Partial<User> do?Partial<T> converts every property in T to optional (adds ? to each one).interface and type in TypeScript?let x: string | number = "hello"; x = 42; x = true;
x = true — The union type string | number allows only strings and numbers. boolean is not in the union, so true causes a compile error.class Person { constructor(public name: string, private age: number) {} }
public, private, protected) in the constructor parameter automatically declares the property, creates it, and assigns the value. It's a shorthand.tsc app.ts — The TypeScript compiler command is tsc. It compiles .ts files into .js files.extends do in this interface?
interface Animal { name: string; } interface Dog extends Animal { breed: string; }
extends means Dog has everything Animal has (name: string) PLUS its own properties (breed: string). Any object typed as Dog must have both.public by default. This means it can be accessed from anywhere.Record<K, V> — Creates a type where all keys are of type K and all values are of type V. Example: Record<string, number> means an object with string keys and number values.