Class Composition
in

Agenda
Software design
TypeScript's versatility
Inheritance and Polymorphism
Composition in Angular
Demo
Andrés Gesteira
X - @GeorgeKaplan_G
Born in Madrid.
Senior Frontend Architect @ Fever
Angular lover.
Likes cinema, books, and food.

Software design
Design concepts
Paradigms
Principles
Patterns
TypeScript's versatility
TypeScript paradigms
- Prototype-based since it is a superset of JavaScript.
- OOP through 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.
- Interface-based programming based on TS's static typing.
Criteria
- Backend projects should apply paradigms.
- Frontend projects should apply patterns.
- Most frontend projects are component-based.
- Backend aligns with business logic.
- Components apply visual logic and associated behavior.
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 Angular
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.
- The best way to apply it is through TypeScript classes.
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.
- However, you cannot access NG's injection context with a decorator, so we will choose mixin classes over them.
Why not using Object.assign()
- You merge properties and methods from plain objects into a single object at runtime.
- Produces a shallow copy of enumerable own properties.
- You lose the type safety.
- Applies mutation if you do not use `{}` as target.
With helper functions

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 Angular
By Andres Gesteira
Class Composition in Angular
- 218