Mastering TypeScript from Scratch – Part 2: Intermediate Concepts


Level up your TypeScript skills by learning interfaces, classes, generics, utility types, and more — the building blocks for real-world projects.

Hey everyone 👋, I’m back again with Part 2 of my TypeScript learning series! If you missed the first part, we covered all the beginner foundations of TypeScript — from setup and basic types to unions and enums. Now, it’s time to take a step forward into the intermediate concepts that make TypeScript powerful in real-world applications. In this part, we’ll explore interfaces vs types, classes and OOP, generics, type narrowing, modules, utility types, DOM typings, and error handling. Buckle up — this is where TS really starts to shine for actual projects! 🚀



📗 Intermediate TypeScript (Core Power)



🔹 1. Interfaces vs Type Aliases Interface

interface User {
  id: number;
  name: string;
}

const user1: User = { id: 1, name: "Usman" };
Enter fullscreen mode

Exit fullscreen mode

Type Alias

type UserType = {
  id: number;
  name: string;
};

const user2: UserType = { id: 2, name: "Ali" };
Enter fullscreen mode

Exit fullscreen mode

Key difference:

  • Interfaces: can be extended & merged.
  • Types: more flexible (unions, intersections), but can’t merge.

Extending Interfaces

interface Person {
  name: string;
}
interface Employee extends Person {
  salary: number;
}

const emp: Employee = { name: "Ali", salary: 50000 };
Enter fullscreen mode

Exit fullscreen mode

Declaration Merging (only interfaces!)

interface Car {
  brand: string;
}
interface Car {
  year: number;
}

const myCar: Car = { brand: "Toyota", year: 2023 };
Enter fullscreen mode

Exit fullscreen mode



🔹 2. Classes & OOP in TS

class Animal {
  public name: string;       // accessible everywhere
  protected age: number;     // accessible in class + subclass
  private secret: string;    // only inside this class
  static count: number = 0;  // belongs to class, not instance
  readonly type: string;     // cannot be reassigned

  constructor(name: string, age: number, type: string) {
    this.name = name;
    this.age = age;
    this.secret = "hidden";
    this.type = type;
    Animal.count++;
  }

  public speak(): void {
    console.log(`${this.name} makes a sound`);
  }
}

class Dog extends Animal {
  constructor(name: string, age: number) {
    super(name, age, "dog");
  }

  public bark() {
    console.log(`${this.name} barks!`);
  }
}

const d = new Dog("Buddy", 3);
d.speak(); // Buddy makes a sound
d.bark();  // Buddy barks!
Enter fullscreen mode

Exit fullscreen mode

Abstract Classes

abstract class Shape {
  abstract area(): number; // must be implemented in subclass
}

class Circle extends Shape {
  constructor(public radius: number) { super(); }
  area(): number {
    return Math.PI * this.radius ** 2;
  }
}

const c = new Circle(5);
console.log(c.area());
Enter fullscreen mode

Exit fullscreen mode



🔹 3. Generics

Generic Function

function identity(arg: T): T {
  return arg;
}

console.log(identity("Hello")); // Hello
console.log(identity(123));     // 123
Enter fullscreen mode

Exit fullscreen mode

Generic Interfaces

interface Box {
  value: T;
}

let stringBox: Box = { value: "text" };
let numberBox: Box = { value: 100 };
Enter fullscreen mode

Exit fullscreen mode

Generic Constraints

function logLength(item: T) {
  console.log(item.length);
}

logLength("hello");  // ✅
logLength([1,2,3]);  // ✅
Enter fullscreen mode

Exit fullscreen mode

Default Generic Types

interface ApiResponse {
  data: T;
}

const res1: ApiResponse = { data: "ok" };   // string by default
const res2: ApiResponse = { data: 42 };
Enter fullscreen mode

Exit fullscreen mode



🔹 4. Type Narrowing

function printId(id: string | number) {
  if (typeof id === "string") {
    console.log("String ID:", id.toUpperCase());
  } else {
    console.log("Number ID:", id.toFixed(2));
  }
}
Enter fullscreen mode

Exit fullscreen mode

Instanceof

class Cat { meow() {} }
class Dog { bark() {} }

function sound(pet: Cat | Dog) {
  if (pet instanceof Cat) pet.meow();
  else pet.bark();
}
Enter fullscreen mode

Exit fullscreen mode

Discriminated Union

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number };

function area(shape: Shape) {
  switch (shape.kind) {
    case "circle": return Math.PI * shape.radius ** 2;
    case "square": return shape.side * shape.side;
  }
}
Enter fullscreen mode

Exit fullscreen mode



🔹 5. Modules & Namespaces

ES Modules

// file: math.ts
export function add(a: number, b: number) {
  return a + b;
}
export default function multiply(a: number, b: number) {
  return a * b;
}

// file: app.ts
import multiply, { add } from "./math";

console.log(add(2, 3));
console.log(multiply(2, 3));
Enter fullscreen mode

Exit fullscreen mode

Namespaces (rarely used now, mostly in legacy code)

namespace Utils {
  export function greet(name: string) {
    return `Hello, ${name}`;
  }
}

console.log(Utils.greet("Usman"));
Enter fullscreen mode

Exit fullscreen mode



🔹 6. Type Assertions & Casting

let value: unknown = "hello";
let strLength: number = (value as string).length;

let el = document.querySelector("#myInput") as HTMLInputElement;
el.value = "typed!";
Enter fullscreen mode

Exit fullscreen mode

Non-null Assertion (!)

let btn = document.querySelector("#btn")!;
btn.addEventListener("click", () => console.log("Clicked!"));
Enter fullscreen mode

Exit fullscreen mode



🔹 7. Utility Types (Built-in)

interface User {
  id: number;
  name: string;
  age?: number;
}

// Make all properties optional
type PartialUser = Partial;

// Make all properties required
type RequiredUser = Required;

// Make all properties readonly
type ReadonlyUser = Readonly;

// Pick selected properties
type UserName = Pick;

// Omit selected properties
type UserWithoutAge = Omit;

// Map keys to type
type Roles = Record<"admin" | "user", boolean>;

// Extract function return/params
function getUser() { return { id: 1, name: "Ali" }; }
type UserReturn = ReturnType;
Enter fullscreen mode

Exit fullscreen mode



🔹 8. Working with DOM & Browser APIs

const input = document.querySelector("#username");
if (input) {
  input.value = "Usman Awan";
}

document.addEventListener("click", (e: MouseEvent) => {
  console.log(e.clientX, e.clientY);
});

document.addEventListener("keydown", (e: KeyboardEvent) => {
  console.log(e.key);
});
Enter fullscreen mode

Exit fullscreen mode



🔹 9. Error Handling Types

Typing try/catch

try {
  throw new Error("Something went wrong");
} catch (err: unknown) {
  if (err instanceof Error) {
    console.error("Error:", err.message);
  }
}
Enter fullscreen mode

Exit fullscreen mode

unknown vs any

  • any: bypasses type checking (unsafe).
  • unknown: forces you to narrow type before using.
let val: unknown = "test";
val.toUpperCase(); // ❌ error
if (typeof val === "string") {
  console.log(val.toUpperCase()); // ✅ safe
}
Enter fullscreen mode

Exit fullscreen mode

✅ Intermediate Summary

You now know how to:

  • Use interfaces vs types, extend & merge.
  • Write classes, abstract classes, OOP with access modifiers.
  • Work with generics for reusable code.
  • Narrow types with typeof, instanceof, discriminated unions.
  • Import/export using modules.
  • Safely cast values & use type assertions.
  • Apply utility types like Partial, Pick, Omit.
  • Handle DOM events with strict types.
  • Properly type error handling.

That’s a wrap for Part 2 (Intermediate) of our TypeScript journey! 🎉 You now know how to confidently use interfaces, classes, generics, utility types, and type-safe DOM handling. With these tools, you’re ready to start applying TypeScript in real projects — whether it’s a Node.js API, a React app, or even a library. In the next and final part (Advanced), we’ll dive into conditional types, mapped types, advanced generics, declaration files, and the latest TS features. Stay tuned, it’s going to be next-level! 💡

Part 1: Beginner Foundations

Thanks for reading! 🙌
Until next time, 🫡
Usman Awan (your friendly dev 🚀)



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *