Thuta Learning
ExercisesProgrammingintermediate

Practice Exercises - Set 2 (Intermediate & Advanced)

Relax. We'll talk through this in plain words — no textbook voice.

What you'll walk away with

  • Work through Practice Exercises - Set 2 (Intermediate & Advanced) yourself
  • Practice and lock in skills you've already learned
  • Get comfortable finding bugs, fixing them, and checking your own work

Let's think about this for a second

This set is tougher than Set 1 — you'll be combining concepts like union types, type narrowing, generics, and classes from the tutorial's Intermediate and Advanced chapters to solve real problems. Each task blends more than one concept together with earlier ones, so pause and think for a few seconds before you start coding. Don't panic if a compiler error shows up — reading the error message and debugging where the type mismatch is happening is itself an important part of learning TypeScript.

Exercises

Task 1: Define a type Shape as the union { kind: "circle"; radius: number } | { kind: "square"; side: number }, and write a function getArea(shape: Shape): number that narrows on kind to calculate the area. Task 2: Create a generic class Stack<T> and implement the methods push(item: T): void, pop(): T | undefined, and peek(): T | undefined. Task 3: Create a base class Animal, extend it with a Dog class, and override the method makeSound(): string (Dog's makeSound() should return "Woof"). Task 4: Write a type guard function isString(value: unknown): value is string, and use it to filter only the string values out of an unknown[] array.

Sample code

typescript
// Task 1
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number };

function getArea(shape: Shape): number {
  // TODO: narrow by shape.kind
  return 0;
}

// Task 2
class Stack<T> {
  private items: T[] = [];
  push(item: T): void {
    // TODO
  }
  pop(): T | undefined {
    // TODO
    return undefined;
  }
  peek(): T | undefined {
    // TODO
    return undefined;
  }
}

// Task 3
class Animal {
  makeSound(): string {
    return "...";
  }
}

class Dog extends Animal {
  // TODO: override makeSound()
}

// Task 4
function isString(value: unknown): value is string {
  // TODO
  return false;
}

const mixed: unknown[] = [1, "two", 3, "four"];
const strings = mixed.filter(isString);
console.log(strings);
You should see
getArea() correctly returns the area for each shape, Dog's makeSound() returns "Woof", and the strings array contains "two" and "four".

5-minute try it yourself

Use Task 2's Stack<T> class as a base class, and add a method isEmpty(): boolean that checks the length of the items array (5 minutes).

A quick word of caution

When writing a type guard function (value is string), don't leave the return type annotation as plain boolean — TypeScript only applies narrowing correctly when you write out the "value is Type" predicate precisely.

Easy traps

  • In Task 1, not understanding why the compiler complains that the property doesn't exist when you try to access shape.radius directly without checking shape.kind first
  • In Task 3, remembering the extends keyword when writing an override method but forgetting a required super() call if there's constructor logic involved

Now try it yourself

Use Task 2's Stack<T> class as a base class, and add a method isEmpty(): boolean that checks the length of the items array (5 minutes).

You'll know it worked when: getArea() correctly returns the area for each shape, Dog's makeSound() returns "Woof", and the strings array contains "two" and "four".

Practice Exercises - Set 2 (Intermediate & Advanced) | Thuta Learning