RizTech Academy logo
RizTech Academy
Design Patterns in TypeScriptLesson 1 of 525 min

What a pattern is, and how types change the calculus

Design patterns are named solutions to recurring problems — a shared vocabulary (say "factory" or "strategy" and another developer knows what you mean) and a toolbox of proven structures. But there is a twist this module is built around, the same one the Kotlin course found: many classic patterns exist to work around limitations of older languages, and TypeScript — with first-class functions, unions, and a structural type system — makes a lot of them dissolve. Learning to recognise when the language has already solved the problem is the real skill.

Where the patterns came from

The famous "Gang of Four" catalogue — Singleton, Factory, Builder, Adapter, Decorator, Strategy, Observer, State — was written in 1994 for C++ and Java. Those languages lacked things we now take for granted: functions were not values, there were no lambdas, no union types, no structural typing. Many "patterns" are, at heart, ceremonies for doing something the language could not do directly.

The Strategy pattern is "pass different behaviour into an object" — which in Java meant an interface, a class per strategy, and wiring. In TypeScript that is a function: (x: number) => number. The pattern did not become wrong; it became a language feature.

How TypeScript changes the calculus

TypeScript has, built in, many of the things the patterns were invented to simulate:

  • Functions are first-class values — so "pass behaviour around" (Strategy, Command, parts of Observer) is just passing a function.
  • Union and discriminated union types — so modelling "one of several kinds" (State, some Visitor uses) is a union with an exhaustive switch, not a class hierarchy.
  • Structural typing — so "make this fit that interface" (Adapter) often needs no wrapper; if the shape matches, it fits.
  • Object literals and spread — so building configured objects (Builder) is often just an object with optional properties.
  • A module system — so a Singleton is often just a module-level value.

The consequence, and the theme of this module: in TypeScript, reach for a language feature before a pattern. A developer fresh from a patterns course tends to apply the heavy Java form — an interface, a factory class, a builder — where a function, a union, or an object literal does the same job in a fraction of the code. Recognising when the language has already solved the problem is what marks someone who understands both the patterns and TypeScript.

But TypeScript is also OO — so some patterns stay

There is a crucial difference from the Kotlin course's stance, and it is worth being honest about: TypeScript is used heavily in object-oriented frameworks — Angular and NestJS are built on classes, decorators, and dependency injection. So unlike a purely functional setting, some patterns genuinely earn their place in TypeScript codebases, especially on the backend:

  • Dependency Injection is central to NestJS and Angular — and it is the pattern that most earns its place (a whole lesson).
  • Factory functions are genuinely useful and idiomatic.
  • Decorator (the pattern and the @ syntax) is everywhere in these frameworks.

So the TypeScript answer is more balanced than "patterns are obsolete": the functional patterns (Strategy, Command, Observer, State) largely dissolve into language features; the OO/architectural patterns (Dependency Injection, Factory) remain useful, especially in framework code. Knowing which is which is the judgement this module teaches.

Why still learn the patterns?

Three solid reasons, so this is not "ignore them":

The vocabulary is real. When you say "the service is a singleton" or "this is a strategy", every developer understands instantly. You will read these names in code, docs, and interviews — you must know what they mean, even when you implement them differently.

The underlying problems are eternal. "How do I vary behaviour without duplicating code?" "How do I construct a complex object safely?" "How do I decouple this from that?" Those questions do not go away — only the mechanics change. The patterns teach you to recognise the problems.

Some patterns still earn their place — in a lighter TypeScript form. A factory function, dependency injection, the observer idea behind reactive libraries. You will use these, written the TypeScript way.

How this module is organised

The rest follows the classic categories, showing the TypeScript reality:

  • Unions over hierarchies — how discriminated unions replace the class hierarchies that State, Visitor, and some Strategy uses would need.
  • Factory and Builder — mostly a function and an object literal, with generics where they help.
  • Dependency Injection — the one pattern that most earns its place, and how decorators power it in NestJS.
  • When not to pattern — the most important skill: recognising when a pattern is over-engineering.

The rule to carry through

For every pattern, ask two questions:

  1. What problem does it solve? (Eternal — learn it.)
  2. Does TypeScript solve it more simply — a function, a union, an object literal — or is it a genuine architectural pattern (like DI) that earns its place?

A pattern applied where a language feature would do is over-engineering. A pattern applied where it genuinely fits, in the lightest form the language allows, is good design. Telling the two apart is what this module teaches.

Check your work

What a design pattern is. A named, reusable solution to a recurring problem — a shared vocabulary and a toolbox.

Where the classic catalogue came from. 1994, for C++ and Java, which lacked first-class functions, unions, and structural typing.

Why many patterns exist. As ceremonies for what older languages could not do directly.

TypeScript's core message. Reach for a language feature (a function, a union, an object) before a pattern — the language often already solves it.

How TypeScript differs from a purely functional setting. It is used in OO frameworks (Angular, NestJS), so some patterns — especially Dependency Injection — genuinely earn their place.

Which patterns dissolve, which remain. Functional patterns (Strategy, Command, Observer, State) largely dissolve; OO/architectural ones (DI, Factory) remain, especially in framework code.

Three reasons to still learn them. The vocabulary is shared knowledge, the problems are eternal, and some earn their place in lighter form.

The two questions to ask of every pattern. What problem does it solve, and does TypeScript solve it more simply or is it a genuine architectural pattern?

Practice

  1. List five patterns you have heard of. For each, guess the problem it solves before this module says.
  2. Explain, in one sentence, why "functions are values" makes the Strategy pattern nearly disappear.
  3. Explain why Dependency Injection is different — an architectural pattern that a language feature does not replace.
  4. Find a piece of Java (online) using a Builder or Factory and imagine the TypeScript version.
  5. Write down the two questions to ask of every pattern, and keep them for the rest of the module.
  6. Recall code you have read that added structure that turned out unnecessary. Note the simpler version.

Official documentation

Next: discriminated unions instead of class hierarchies.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship