Typescript
Module 1: Setup
1. Meet TypeScript
JavaScript with types. It checks your code while you type, then turns into plain JavaScript.
- Made by: Anders Hejlsberg at Microsoft, the designer of C#
- Released: 2012, free and open source
- Today: the most used language on GitHub since August 2025 (Octoverse)
- Used in: React, Next.js, Node.js and almost every modern framework
Why it exists
- JavaScript finds mistakes when the code runs, often in front of your users.
- TypeScript finds them while you type.
The problem
// JavaScript: crashes when it runsconst user = { name: "Ahmed", email: "ahmed@email.com" };user.emial.toLowerCase();// π₯ TypeError: Cannot read properties of undefined// TypeScript: caught while you typeconst user = { name: "Ahmed", email: "ahmed@email.com" };user.emial.toLowerCase();// β Property 'emial' does not exist. Did you mean 'email'?2. Install Node.js
You need Node.js to run npm, TypeScript and your code.
- Go to nodejs.org and download the LTS version.
- Run the installer and follow the steps.
- Open a new terminal and check it worked:
node -vnpm -v- Both commands should print a version number.
- npm comes with Node.js, so there's nothing extra to install.
- Only download from the official site.
3. Option 1: The classic way (tsc)
You compile by hand. It's the best way to see what TypeScript actually does.
- Open VS Code and open a new folder for your project, for example ts-basics.
- Open the terminal: Terminal β New Terminal.
- Run:
npm init -y- This creates a package.json file for your project.
npm install -g typescripttsc -v- g installs it globally, so tsc works in any folder.
- tsc -v prints the version, which confirms it's installed.
- Permission error on Mac? Use sudo npm install -g typescript.
Create index.ts:
let message: string = "Salaam, TypeScript!";console.log(message);tsc index.tsnode index.js- tsc = the TypeScript compiler. It checks your types, then creates index.js next to index.ts.
- node index.js runs the JavaScript.
Salaam, TypeScript!Change the value to a number and compile again:
let message: string = 42;error TS2322: Type 'number' is not assignable to type 'string'.- The error points to the file, line and problem.
- Fix it, compile again, and the error is gone.
Tired of running tsc after every change? Let it watch your file:
tsc index.ts --watch- It recompiles every time you save and shows errors live.
- Run node index.js whenever you want to see the output.
- Stop it with Ctrl + C.
4. Option 2: The modern way (Vite)
Real projects don't compile by hand. Vite handles it for you:
npm create vite@latest ts-vite -- --template vanilla-tscd ts-vitenpm installnpm run devOpen src/main.ts, make a change, save, and the browser updates instantly.
- Vite runs your TypeScript but doesn't type-check it while running. Errors show up as red underlines in VS Code.
- npm run build runs a full type check before building, and stops if anything is wrong.
Summary
| Command | What it does |
|---|---|
| npm install -g typescript | Installs the TypeScript compiler |
| tsc -v | Checks that it's installed |
| tsc index.ts | Type-checks and creates index.js |
| node index.js | Runs the JavaScript |
| tsc index.ts --watch | Recompiles on every save |
| npm create vite@latest | Creates a Vite project |
| npm run dev | Starts Vite with live reload |
| npm run build | Type-checks and builds for production |
Module 2: Types & Functions
TypeScript is JavaScript with types. You say what kind of value goes where, and TypeScript flags mistakes in your editor before the code ever runs.
1. Annotations
Write : type after a name to set what it can hold.
let name: string = "Ahmed";name = "Sahra"; // β
still a stringname = 25; // β Type 'number' is not assignable to type 'string'- Errors show up in VS Code and when you run tsc, not in the browser.
2. Primitives
// string: text, in quotes or backtickslet name: string = "Ahmed";let greeting: string = `Salaam, ${name}`;
// number: whole numbers and decimalslet age: number = 25;let price: number = 99.5;
// boolean: true or falselet isStudent: boolean = true;let hasPaid: boolean = false;
age = "25"; // β string is not a numberisStudent = "yes"; // β string is not a boolean- Always lowercase: string, not String.
- number covers both 25 and 99.5. There is no separate int or float.
3. Inference
Give a value straight away and TypeScript works out the type for you.
let city = "Oslo"; // stringlet count = 0; // numberlet isOpen = false; // boolean
city = 42; // β error, even without an annotation
let score: number; // no value yet, so annotate- Value right away β skip the annotation.
- No value yet β annotate.
- Hover over a variable in VS Code to see its type.
4. Arrays
Put [] after the type for a list of that type.
let names: string[] = ["Ahmed", "Sahra"];let scores: number[] = [90, 75, 60];let ages = [20, 25]; // inferred: number[]
names.push("Omar"); // β
names.push(42); // β number is not a string- Every item must match the type.
5. any
any switches type checking off for that value.
let data: any = "hello";data = 42; // no errordata.toUpperCase(); // no error here, but it crashes when it runs- Avoid it. Using any means you are back to plain JavaScript.
6. Functions
Type each parameter, then the return value after the ).
function add(a: number, b: number): number { return a + b;}
add(5, 10); // β
15add(5, "10"); // β "10" is not a number
// Arrow functionconst greet = (name: string): string => `Salaam, ${name}`;
// Returns nothing β voidfunction logMessage(message: string): void { console.log(message);}- Parameters always need a type.
- The return type can be inferred, but writing it makes your intent clear.
7. Optional & default parameters
// ? β can be left outfunction greet(name: string, title?: string): string { return title ? `Salaam, ${title} ${name}` : `Salaam, ${name}`;}
greet("Ahmed"); // "Salaam, Ahmed"greet("Ahmed", "Dr."); // "Salaam, Dr. Ahmed"
// = value β used when left outfunction calculateTip(bill: number, percent = 10): number { return bill * (percent / 100);}
calculateTip(500); // 50calculateTip(500, 20); // 100- A left-out optional parameter is undefined.
- A default's type is inferred from its value.
- Optional and default parameters go after the required ones.
Summary
| Topic | Syntax | Remember |
|---|---|---|
| Annotations | let x: type = value | Errors appear before the code runs |
| Primitives | string, number, boolean | Always lowercase |
| Inference | let x = value | Skip the annotation when there's a value |
| Arrays | string[], number[] | Every item must match |
| any | let x: any | Avoid it, it turns checking off |
| Functions | (a: number): number | Parameters always need a type |
| void | (): void | For functions that return nothing |
| Optional | title?: string | Can be left out, becomes undefined |
| Default | percent = 10 | Used when left out, type inferred |
Challenges
You have $4,000 in savings and want to know your zakat. Write getZakat(savings, rate) that returns the amount to pay. Most people pay 2.5%, so make that the default rate.
Expected output
Savings: $4000Zakat: $100Your flight costs $500 and the booking fee is $50. Write getTotal(price, fee) that returns the price plus the fee.
Expected output
Flight: $500Fee: $50Total: $550The gym costs $50 a month and your budget is $320. Write getTotalCost(fee, months) that returns the cost, and canAfford(budget, cost) that returns true if your budget covers it.
Expected output
Cost for 6 months: $300Can afford: trueYou sent money to your family three times this month: $100, $150 and $50. Store the amounts in a typed array, then write getTotal(transfers) that returns how much you sent in total.
Expected output
Sent this month: $300Your friends have birthdays this week. Write wishBirthday(name, age) that prints a birthday message. You don't always know their age, so make it optional. The function returns nothing.
Expected output
Happy birthday, Hodan!Happy birthday, Ahmed! You're 25 today.Module 3: Objects
Most real data is an object: a user, a product, an order. TypeScript lets you describe its shape once, then checks it everywhere.
1. Object types
List each property and its type inside { }.
let student: { name: string; age: number } = { name: "Hodan", age: 22 };
student.age = "23"; // β wrong typestudent.email; // β property doesn't exist- TypeScript catches missing properties, wrong types and unknown properties.
2. Type aliases
Name a shape with type and reuse it.
type Product = { name: string; price: number };
const laptop: Product = { name: "MacBook Air", price: 12990 }; // β
const phone: Product = { name: "iPhone" }; // β missing price- Type names start with a capital letter.
3. Interfaces
The other way to describe an object. It has no =.
interface User { username: string; followers: number;}
const user: User = { username: "abdi_codes", followers: 1200 };- type and interface do the same job for objects. Pick one and stay consistent.
- Only type can do literal unions (section 6).
4. Optional properties
Add ? to a property that may be missing.
type Profile = { name: string; bio?: string };
const a: Profile = { name: "Sahra" }; // β
const b: Profile = { name: "Omar", bio: "Frontend dev" }; // β
- A missing optional property is undefined.
5. Objects in functions
Use your type as a parameter or return type.
type Movie = { title: string; year: number };
function describe(movie: Movie): string { return `${movie.title} (${movie.year})`;}
describe({ title: "Dune", year: 2021 }); // "Dune (2021)"describe({ title: "Dune" }); // β missing year- Inside the function, movie. gives you autocomplete.
6. Literal unions
Use | to allow only exact values.
type Status = "pending" | "paid" | "failed";
let status: Status = "paid"; // β
status = "refunded"; // β not allowed- Works inside objects too: role: "admin" | "viewer".
- Case matters: "Paid" is not "paid".
7. All together
type Item = { name: string; price: number };
type Order = { customer: string; items: Item[]; // list of objects status: "cooking" | "delivered"; // literal union note?: string; // optional};
const order: Order = { customer: "Ayaan", items: [ { name: "Suqaar", price: 189 }, { name: "Sambusa", price: 25 }, ], status: "cooking",};Summary
| Topic | Syntax | Remember |
|---|---|---|
| Object type | { name: string } | Catches missing, wrong and unknown properties |
| Type alias | type Product = { ... } | Name it once, reuse it |
| Interface | interface User { ... } | Same job as type, pick one |
| Optional | bio?: string | Can be missing, becomes undefined |
| Functions | (movie: Movie): string | Autocomplete and typo checks |
| List of objects | Item[] | Every item must match |
| Literal union | "paid" | "failed" | Only these exact values |
Challenges
You're making your student ID. Create a Student type with name, age and city. Then create yourself as a student and print each value.
Expected output
Name: HodanAge: 22City: OsloYou're saving two contacts. Ahmed has a phone number and an email, but Nimo only has a phone number. Create a Contact type where the email is optional, then save both contacts.
Expected output
Ahmed - 92345678 - ahmed@email.comNimo - 98765432You ordered food from a delivery app. Create an Order type with dish and status. The status can only be "received", "cooking" or "delivered". Then write getOrderUpdate(order) that returns a short update.
Expected output
Your Suqaar is cookingYour cart has rice ($25), chicken ($40) and milk ($6). Create a Product type with name and price, store the cart as a Product[], and write getCartTotal(cart) that returns the total.
Expected output
Items: 3Total: $71A new user signs up for your app. Create a User interface with username, email, followers and a role that's either "admin" or "viewer". Then write createUser(username, email) that returns the new user, starting with 0 followers and the role "viewer".
Expected output
Username: abdi_codesEmail: abdi@email.comFollowers: 0Role: viewer