Most modern JavaScript and TypeScript development leans heavily on functions and plain objects. But classes, interfaces, and inheritance aren't going away. You'll encounter them in libraries, frameworks, and codebases across the industry, and if you don't understand how they work, you'll struggle to work with the code that uses them.
The problem is that without a clear mental model, you'll misuse inheritance where composition would be simpler, skip interfaces where they'd save you from brittle coupling, and create class hierarchies that become impossible to follow three levels deep.
In this workshop, you'll learn how to build classes with proper encapsulation, implement interfaces for polymorphism, extend base classes when inheritance makes sense, and reach for composition when it doesn't.
Every section pairs the "how" with the "why," so you don't just learn the syntax, you learn when each pattern earns its place and when simpler alternatives are the better choice.
Classes
Classes are the fundamental building block of object-oriented TypeScript. They bundle data and behavior together into a single unit-- a user has a name and email, a shopping cart has items and a total. If you've only worked with plain objects and functions, classes introduce a new way to organize your code.
In this section, you'll build classes from scratch while learning how they work under the hood:
- Creating classes with properties, constructors, and methods
- Using
newto create instances and understanding whatthisrefers to - Initializing properties with default values instead of constructor arguments
- Making fields private with the
#syntax to control access - Refactoring constructors to accept an options object instead of positional arguments
Interfaces and Classes
An interface on its own is just a shape-- a contract that says "anything claiming to be this type must have these methods." The real power shows up when multiple classes implement the same interface, because now you can write functions that accept any of them without knowing which specific class they'll get.
This section teaches you how interfaces enable polymorphism in practice:
- Defining interfaces with method signatures that classes must implement
- Using the
implementskeyword to enforce a contract on a class - Implementing multiple interfaces on a single class
- Programming to abstractions by accepting interfaces instead of concrete classes
- Writing functions that work with any implementation of a given interface
Inheritance and Polymorphism
Inheritance lets one class build on another. A circle is a shape. A square is a rectangle. The child class inherits everything from the parent and can override specific behavior-- like calculating area differently for each shape.
Here you'll work through the mechanics of inheritance and understand where it helps and where it hurts:
- Extending a base class with the
extendskeyword - Forwarding constructor arguments to the parent with
super - Overriding methods to provide specialized behavior in child classes
- Using substitutability to pass any child class where the parent is expected
- Understanding why deep inheritance chains become difficult to maintain
Composition vs Inheritance
Inheritance says "this thing is a that." Composition says "this thing has a that." A car isn't an engine-- it has one. The distinction matters because inheritance creates tight coupling between parent and child, while composition lets you swap out parts without rewriting the class that uses them.
This section shows you when to reach for each approach and why composition is usually the better default:
- Distinguishing "is a" relationships (inheritance) from "has a" relationships (composition)
- Injecting dependencies through constructor arguments instead of hardcoding them
- Swapping implementations at runtime by passing different objects (file logger, console logger, silent logger)
- Using dependency injection to make classes testable with mock objects
- Defaulting to composition and reserving inheritance for clear "is a" cases

