TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
201: Mixin Classes Explained
You've probably hit a wall where you want a class to do two different things, but TypeScript (and JavaScript) only lets you extend one single class. I see this all the time when people are building game engines or complex UI components.
// The "Dream" Code (which doesn't actually work)
class Entity {
id: string = Math.random().toString();
}
class Positionable {
x: number = 0;
y: number = 0;
move(dx: number, dy: number) { this.x += dx; this.y += dy; }
}
class Damageable {
hp: number = 100;
takeDamage(amt: number) { this.hp -= amt; }
}
// ❌ Error: Classes can only extend a single class.
class Player extends Entity, Positionable, Damageable {
name: string = "Hero";
}
The Multiple Inheritance Wall
The code above is a classic mistake. You're trying to use multiple inheritance, which exists in languages like C++, but is a hard "no" in the JavaScript ecosystem. You might be tempted to just use interfaces here, but interfaces don't provide implementation. If you use an interface for Damageable, you'll find yourself copy-pasting the takeDamage logic into every single class that needs it. That's a maintenance nightmare waiting to happen.
So, how do we share behavior across unrelated classes without creating a giant, fragile inheritance tree? We use Mixins.
The Class Factory Pattern
In TypeScript, a Mixin isn't a special keyword; it's just a function that takes a class and returns a new class that extends the one it was given. Think of it as a "class factory."
To make this work with TypeScript's type system, we first need a helper type to describe what a "constructable" class looks like. I usually stick this in a utilities file:
type Constructor = new (...args: any[]) => T;
Now, instead of defining Positionable as a class, we define it as a function that wraps a base class:
function PositionableMixin<TBase extends Constructor>(Base: TBase) {
return class extends Base {
x = 0;
y = 0;
move(dx: number, dy: number) {
this.x += dx;
this.y += dy;
console.log(`Moved to ${this.x}, ${this.y}`);
}
};
}
Stacking Behaviors
The magic happens when you compose these mixins. Because each mixin returns a class, you can nest them. It looks a bit weird at first—like an onion—but it's incredibly powerful. You're essentially building a custom class hierarchy on the fly.
function DamageableMixin<TBase extends Constructor>(Base: TBase) {
return class extends Base {
hp = 100;
takeDamage(amt: number) {
this.hp -= amt;
console.log(`Took ${amt} damage. HP now: ${this.hp}`);
}
};
}
// Now we compose them.
// We start with Entity, wrap it in Positionable, then wrap that in Damageable.
class Player extends DamageableMixin(PositionableMixin(Entity)) {
name = "Hero";
}
const player = new Player();
player.move(10, 5); // From PositionableMixin
player.takeDamage(20); // From DamageableMixin
console.log(player.id); // From Entity
I'll be honest: the syntax DamageableMixin(PositionableMixin(Entity)) can get ugly if you have six or seven mixins. But it solves the fundamental problem. You've decoupled the "ability to move" from the "ability to take damage," and you can now apply those abilities to any class—be it a Player, a DestructibleWall, or a FlyingNPC—without worrying about where they fit in a rigid tree.
📋 Practical Task
Exercise: Building a Plugin-based Logging System with Mixins
You are building a logging system. You have a base Logger class that simply prints a message to the console. However, different environments need different "plugins" (capabilities) added to the logger without rewriting the base logic.
Your Task:
- Create a base class
Loggerwith a methodlog(message: string): voidthat prints the message. - Implement a
TimestampMixinthat overrides thelogmethod to prepend the current ISO date string to the message. - Implement a
ColorizeMixinthat overrides thelogmethod to wrap the message in ANSI color codes (e.g.,\x1b[32m${message}\x1b[0mfor green). - Create a class
EnhancedLoggerthat uses both mixins. - Instantiate
EnhancedLoggerand calllog("System initialized"). The output should be both timestamped and colored.
Hint: Remember that the order of mixins matters! The last mixin wrapped is the first one to intercept the method call.
There are no comments for now.