Language lab · Intermediate
🔷 TypeScript
Concept → trace → write code → run tests → explain why → improve → mini-project. Start in the browser if you like; set up your own computer when you are ready.
📖 Topics with real code (12)
Every verified output below was produced by running the code with Version 5.6.3 on 5 Nov 2026. Open a topic to edit the code and run it right here in your browser.
🌱 Beginner (4)
Types and type inference
TypeScript adds types to JavaScript and checks them before the code runs. Annotate with : type, or let the compiler infer. Union types allow one of several types.
let age: number = 15; const studentName: string = "Asha"; const isStudent: boolean = true; let id: string | number = 42; // union id = "S-42"; const marks = [88, 95]; // inferred number[] console.log(studentName, age, isStudent, id, marks, typeof id); const tuple: [string, number] = ["Ravi", 95]; console.log(tuple[0].toUpperCase(), tuple[1].toFixed(1));
✅ Verified output
Asha 15 true S-42 [ 88, 95 ] string RAVI 95.0
Functions with typed parameters
Parameters and return values carry types. Optional parameters use ?, defaults use =. The compiler refuses a call with the wrong types.
function area(length: number, width: number = 1): number {
return length * width;
}
function greet(name: string, title?: string): string {
return title ? `Namaste, ${title} ${name}!` : `Namaste, ${name}!`;
}
const average = (...nums: number[]): number => nums.reduce((s, x) => s + x, 0) / nums.length;
console.log(area(5, 3), area(7), greet("Ravi"), greet("Meena", "Dr."), average(4, 9, 2, 7));
✅ Verified output
15 7 Namaste, Ravi! Namaste, Dr. Meena! 5.5
Conditionals and narrowing
Inside an if that checks typeof or a property, TypeScript narrows the type, so only safe operations are allowed in each branch.
function describe(value: string | number | boolean): string {
if (typeof value === "string") return `text of length ${value.length}`;
if (typeof value === "number") return value >= 35 ? "pass mark" : "fail mark";
return value ? "yes" : "no";
}
for (const v of ["hello", 78, 20, false]) console.log(v, "->", describe(v));
✅ Verified output
hello -> text of length 5 78 -> pass mark 20 -> fail mark false -> no
Loops over typed arrays
for...of, forEach and classic for loops work as in JavaScript, but the element type is known, so typos are caught.
const subjects: string[] = ["maths", "science", "telugu"];
for (const s of subjects) console.log(s.toUpperCase());
subjects.forEach((s, i) => console.log(i, s.length));
let total = 0;
for (let i = 1; i <= 10; i++) {
if (i % 2) continue;
total += i;
}
console.log("sum of evens", total);
✅ Verified output
MATHS SCIENCE TELUGU 0 5 1 7 2 6 sum of evens 30
🌿 Intermediate (4)
Interfaces and type aliases
An interface describes the shape of an object; readonly fields cannot change; optional fields use ?.
interface Student {
readonly roll: string;
name: string;
marks: number;
email?: string;
}
type Grade = "A+" | "A" | "Pass" | "Fail";
function grade(s: Student): Grade {
return s.marks >= 90 ? "A+" : s.marks >= 75 ? "A" : s.marks >= 35 ? "Pass" : "Fail";
}
const list: Student[] = [{ roll: "R1", name: "Asha", marks: 88 }, { roll: "R2", name: "Ravi", marks: 95, email: "r@example.edu" }];
for (const s of list) console.log(s.name, grade(s), s.email ?? "no email");
✅ Verified output
Asha A no email Ravi A+ r@example.edu
Classes with access modifiers
private, protected and public control access at compile time; parameter properties declare and assign fields in one line.
class BankAccount {
constructor(public readonly owner: string, private balance: number = 0) {}
deposit(amount: number): void {
if (amount <= 0) throw new RangeError("amount must be positive");
this.balance += amount;
}
get current(): number {
return this.balance;
}
}
const acc = new BankAccount("Asha", 500);
acc.deposit(250);
console.log(acc.owner, acc.current);
✅ Verified output
Asha 750
Inheritance, abstract classes and interfaces
An abstract class cannot be created directly; subclasses must implement its abstract members. A class can implement several interfaces.
interface Named { name(): string; }
abstract class Shape implements Named {
abstract area(): number;
name(): string { return this.constructor.name; }
describe(): string { return `${this.name()} ${this.area().toFixed(2)}`; }
}
class Circle extends Shape { constructor(private r: number) { super(); } area() { return Math.PI * this.r ** 2; } }
class Rect extends Shape { constructor(private w: number, private h: number) { super(); } area() { return this.w * this.h; } }
const shapes: Shape[] = [new Circle(1), new Rect(3, 4)];
shapes.forEach((s) => console.log(s.describe()));
✅ Verified output
Circle 3.14 Rect 12.00
Errors and the unknown type
catch gives unknown: check it with instanceof before use. Custom error classes carry extra data.
class ValidationError extends Error {
constructor(public field: string, message: string) {
super(message);
this.name = "ValidationError";
}
}
function parseAge(text: string): number {
const n = Number(text);
if (!Number.isInteger(n) || n < 0 || n > 120) throw new ValidationError("age", `bad age: ${text}`);
return n;
}
for (const input of ["15", "abc", "200"]) {
try {
console.log("age", parseAge(input));
} catch (e: unknown) {
if (e instanceof ValidationError) console.log(e.name, e.field, e.message);
}
}
✅ Verified output
age 15 ValidationError age bad age: abc ValidationError age bad age: 200
🌳 Advanced (4)
Generics
A generic function or class keeps the exact type of its input. Constraints (extends) limit which types are allowed.
function first<T>(items: T[]): T | undefined {
return items[0];
}
function maxBy<T>(items: T[], key: (t: T) => number): T {
return items.reduce((best, x) => (key(x) > key(best) ? x : best));
}
class Stack<T> {
private items: T[] = [];
push(x: T) { this.items.push(x); return this; }
pop(): T | undefined { return this.items.pop(); }
get size() { return this.items.length; }
}
console.log(first([3, 1]), first(["a", "b"]), maxBy([{ n: "Asha", m: 88 }, { n: "Ravi", m: 95 }], (s) => s.m).n);
const st = new Stack<string>().push("a").push("b");
console.log(st.pop(), st.size);
✅ Verified output
3 a Ravi b 1
Utility and mapped types
Built-in utility types (Partial, Pick, Record, Readonly) build new types from old ones. keyof gives the property names.
interface Course { code: string; title: string; credits: number; }
type CourseUpdate = Partial<Course>;
type CourseCard = Pick<Course, "code" | "title">;
const catalog: Record<string, Course> = { CS101: { code: "CS101", title: "Python", credits: 4 } };
function update(c: Course, patch: CourseUpdate): Course {
return { ...c, ...patch };
}
function getField<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const updated = update(catalog.CS101, { credits: 5 });
const card: CourseCard = { code: updated.code, title: updated.title };
console.log(updated, card, getField(updated, "credits"));
✅ Verified output
{ code: 'CS101', title: 'Python', credits: 5 } { code: 'CS101', title: 'Python' } 5
Discriminated unions
A shared literal field (kind) lets TypeScript know exactly which variant you have in each switch branch; the never check makes the switch exhaustive.
type Shape =
| { kind: "circle"; r: number }
| { kind: "rect"; w: number; h: number }
| { kind: "triangle"; b: number; h: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.r ** 2;
case "rect": return s.w * s.h;
case "triangle": return 0.5 * s.b * s.h;
default: {
const unreachable: never = s;
return unreachable;
}
}
}
const all: Shape[] = [{ kind: "circle", r: 1 }, { kind: "rect", w: 3, h: 4 }, { kind: "triangle", b: 6, h: 2 }];
console.log(all.map((s) => `${s.kind}=${area(s).toFixed(2)}`).join(" "));
✅ Verified output
circle=3.14 rect=12.00 triangle=6.00
Typed async code
async functions return Promise<T>; the awaited value keeps its type. Promise.all on a typed tuple gives a typed tuple back.
interface Weather { city: string; tempC: number; }
const wait = (ms: number) => new Promise<void>((ok) => setTimeout(ok, ms));
async function fetchWeather(city: string): Promise<Weather> {
await wait(100);
return { city, tempC: city.length * 4 };
}
async function main(): Promise<void> {
const [a, b] = await Promise.all([fetchWeather("Pune"), fetchWeather("Hyderabad")]);
console.log(`${a.city} ${a.tempC}C, ${b.city} ${b.tempC}C`);
}
main();
✅ Verified output
Pune 16C, Hyderabad 36C
🛠️ Set up and verify
- Install
npm install -g typescript
npm install -g typescript
npm install -g typescript
- Editor: Visual Studio Code with ESLint, Prettier (TypeScript support is built into VS Code).
- Create
hello.tsconst who: string = 'GayaGnan'; console.log(`Hello, ${who}!`); - Run it and check the version
tsc hello.ts && node hello.js tsc --version
- Health check: paste what your terminal printed.
Install one language at a time. These commands only install or print versions; nothing here deletes files. Versions change: check the official page and note the version you installed.
🧭 The 14-lab path
- 00 · Setup and verify — install the runtime and editor, run hello world, print the version, fix PATH
- 01 · Syntax and trace — follow each line and the value of every variable
- 02 · Logic — conditionals and decision trees
- 03 · Loops — trace tables and loop challenges
- 04 · Functions — break problems down and test each piece
- 05 · Data structures — lists, maps, stacks, queues and trees you can see
- 06 · Debugging — bug cases with a hint ladder
- 07 · Algorithms — search, sort and Big-O comparison
- 08 · Files and data — read and write fixture files
- 09 · API or database — talk to a mock API or a sandbox database
- 10 · Testing — write tests and read failing assertions
- 11 · Security — secure coding and dependency hygiene for this language 🧪 TypeScript Escape Room
- 12 · Project — one portfolio mini-project
- 13 · Assessment — quiz, coding test, rubric and an explain-your-answer prompt
📚 Modules
- primitive types
- unions
- interfaces
- generics
- narrowing
- typed functions
- async typing
- modules
- API schemas
- React props and state typing
- tests
🏗️ Mini-projects
- Typed quiz engine
- API client with validation
- React student dashboard
- Event workflow simulator
Projects use synthetic data only.