Class Composition
in TypeScript
https://slides.com/agesteira/typescript-class-composition

https://quantum-quirks.hashnode.dev/class-composition-in-typescript
Agenda
Software design
TypeScript's versatility
Inheritance and Polymorphism
Composition in frameworks
Decorators & Mixins Demo
Andrés Gesteira
Twitter: @GeorgeKaplan_G
Born in Madrid.
Senior Software Engineer
Angular developer.
Likes cinema, books, and food.

Software design
Design concepts
Paradigms
Principles
Patterns
TypeScript's versatility
JavaScript paradigms
- Initially designed as prototype-based.
- Procedural programming: a function for each task.
- Object-oriented programming: constructor functions or ES6 classes.
- Functional programming: avoiding mutable data.
- Event-driven programming with e.g. mouse and load events.
- Asynchronous programming with e.g. callbacks and promises.
- Reactive programming with observable streams.
TS type-driven development
- Not a paradigm but a development methodology.
- Supporting all mentioned paradigms...
- ...but enhanced with static typing.
- TypeScript features (type inference, interfaces, enums, etc) do enforce the interface-based programming paradigm.
Criteria
- Backend should projecst apply paradigms.
- Frontend projects maybe should not.
- Most FE projecst are component-based.
- Business logic aligns with BE.
- Component logic should apply patterns, not paradigms.
Inheritance and Polymorphism
Stay D.R.Y.

Be an animal
abstract class Animal {
abstract makeSound(): void;
move(distanceInMeters: number): void {
console.log(`This animal moved ${distanceInMeters} meters.`);
}
}
class Dog extends Animal {
makeSound(): void {
console.log('The dog barks.');
}
}
class Cat extends Animal {
makeSound(): void {
console.log('The cat meows.');
}
}
const dog = new Dog();
const cat = new Cat();
dog.makeSound(); // Output: The dog barks.
dog.move(10); // Output: This animal moved 10 meters.
cat.makeSound(); // Output: The cat meows.
cat.move(5); // Output: This animal moved 5 meters.
Any animal
- Humanity hates snakes, but they are also animals.
- They are just silent animals.
- So we need 2 abstractions in hierarchy chain:
- NoisyAnimal.
- SilentAnimal.
- Which would create 2 very (too) similar sub-chains.
Avoid abstract classes
- They can only be extended by one other class.
- They break the Liskov substitution principle.
- Hierarchy chains can become very complex.
- Polymorphism may involve runtime method resolution.
- Code can get very difficult to debug.
Composition in frameworks
Composition vs Inheritance
- Composition offers a more flexible approach.
- It follows the mixin pattern.
- Mixins are objects that can add reusable functionality to another object or class, without using inheritance.
- This pattern is widely used in frontend frameworks.
- The best way to apply it is through TypeScript classes.
No TS class frameworks
- React and React-like ones. They declare functions. But they used to do HOCs some time ago.
- Vue. You need third party libraries for that, but they are disregarded as per version 3.
- Svelte. Its particular syntax with pseudo-html script tags does not allow it.
TS class frameworks



NG Directive composition API
Allows us to apply Angular directives to a component's host element from within this component's TypeScript class.
It does not allow us to use properties or methods from any host directive directly in the component class.
@Component({
standalone: true,
[...],
hostDirectives: [MenuBehaviorDirective],
})
export class AdminMenuComponent {
menuOpen: boolean = inject(MenuBehaviorDirective).menuOpen;
}
Mixins and decorators
- The only 2 effective ways of doing TS class composition.
- Mixins are explicitly designed for composition, whereas decorators are more inclined towards metaprogramming by annotating classes or class members.
- Decorators are currently at stage 3 of the TC39 process.
- TypeScript 5 can implement the stage 3 spec without using any compiler options for it.
Demo
Conclusion
Use mixins for class compositions that involve either adding or replacing class members. However, do use decorators for edge cases that may involve initializing or modifying functionality.
Class Composition in TypeScript
By Andres Gesteira
Class Composition in TypeScript
- 250