Do you need classes in JS/TS? 2025 version
Hi there folks! This is a revisited version of 2022 article: Do you need classes in JS/TS?. No AI was used neither for the article nor for the cover image :). If you are starting out as a JavaScript/TypeScript developer, you might run into these conflicting statements, like you should write "functional style" (FP) on the frontend, but "serious backend work requires object oriented development (OOP)". You might even encounter backend frameworks that are fully object-oriented. You might feel something is off, or there are too many steps required to do simple things, but you might not have the experience yet to pinpoint the problem. Let me help you with that! The point of using a class Let's roll back time and go back to an old language: C. The algorithm you are working on requires an implementation of a stack and you have to write one from scratch. You drop in elements into the stack and using a pop() function you retrieve the last element that was dropped into it. To implement it you would need the following things: some kind of storage that holds the items of the stack some way to deal with memory when you have a lot of item in your stack since it is not trivial to manage all that, you want the user to only use specific methods that you provide Therefore you will have the following: a private internal state that holds values between calls (the stack stays in memory and you can access at your leisure) a couple of hidden (private) methods to make sure that building blocks of the internal state is managed properly and the users won't base their code on the "Implementation of the Day" a couple of public methods that lets you interact with this unit and you could optionally have an interface which declares what are the mandatory bits of a stack so when you roll out the even more efficient StackV2 data structure it will be just a drop-in replacement Now imagine when this was a new thing! It was very elegant, very organized. People back then were dealing with very low level things like buffers, pointers, etc. and it made sense to have more of classes. The rise of Object Oriented Programming As with any trends, the industry got a bit overboard with it. When C# and Java arrived it was no longer possible to just declare a procedure or function without having a class; in my opinion it was a way to discourage people from Procedural Programming which was the old paradigm. But for dead simple data transformation code where you process data from type A to type B you do not have an internal state made of low level building blocks. Therefore, while OOP solved some very important problems it also introduced bureucracy that got imprinted in the brains of a generation that thinks mostyl in classes as the way for everything. Good example: database connection Managing access to a database usually needs maintaining some kind of a connection pool (reusing a number of connections to a database server). It also needs to track if we have already logged in or not, if the remote server is down and so on. But we don't want to know about all that, we just need to know how to configure the DB access and how to run queries. So those functions are the ones that are going to be our interface: a set of functions and properties that do not change frequently (or at all preferably) while want to keep it from peering eyes how we implemented the connection pool, etc. (making it private). Moreover, we will have to persist a state: how's the database access? Did we log in already, is the server up, etc. // An overly simplified database module interface Database { configure: (options: ConfigOptions) => void; connect: (user: string, password: string) => void; query: (query: string) => T; } class PostgresDatabase implements Database { // ... } // Same public methods but maybe very different concrete implementation! class MySqlDatabase implements Database { // ... } Bad example: REST handlers Let's say you have an FAQ page on your company website. Each user comes in and for wants to see an article. Here we maybe have a projection of (userId, articleId, language) => article. For this a simple function with its isolated scope would be a great choice as you don't want to persist state between calls. I have a little story. We were new to next.js about 6 years ago and 2 team separately made the same mistake: persisting a state between calls unwittingly. For my team, we managed to serve articles in a random language, depending on the last user's setting - as we were persisting in a local state the language, it was foolish of course. The other team had it worse: unlike us they didn't catch it in QA and they managed to deploy to prod a bug where users could see other users' private data because of the same general mistake. So here we don't want to keep a state between calls and our interface is also very simple: 1 function with a specific signature (see the projection above). We mig

Hi there folks!
This is a revisited version of 2022 article: Do you need classes in JS/TS?. No AI was used neither for the article nor for the cover image :).
If you are starting out as a JavaScript/TypeScript developer, you might run into these conflicting statements, like you should write "functional style" (FP) on the frontend, but "serious backend work requires object oriented development (OOP)".
You might even encounter backend frameworks that are fully object-oriented. You might feel something is off, or there are too many steps required to do simple things, but you might not have the experience yet to pinpoint the problem.
Let me help you with that!
The point of using a class
Let's roll back time and go back to an old language: C
. The algorithm you are working on requires an implementation of a stack and you have to write one from scratch. You drop in elements into the stack and using a pop()
function you retrieve the last element that was dropped into it.
To implement it you would need the following things:
- some kind of storage that holds the
items
of thestack
- some way to deal with memory when you have a lot of
item
in yourstack
- since it is not trivial to manage all that, you want the user to only use specific methods that you provide
Therefore you will have the following:
- a
private
internalstate
that holds values between calls (the stack stays in memory and you can access at your leisure) - a couple of hidden (
private
) methods to make sure that building blocks of the internalstate
is managed properly and the users won't base their code on the "Implementation of the Day" - a couple of
public
methods that lets you interact with this unit - and you could optionally have an
interface
which declares what are the mandatory bits of astack
so when you roll out the even more efficientStackV2
data structure it will be just a drop-in replacement
Now imagine when this was a new
thing! It was very elegant, very organized. People back then were dealing with very low level things like buffers, pointers, etc. and it made sense to have more of classes.
The rise of Object Oriented Programming
As with any trends, the industry got a bit overboard with it. When C# and Java arrived it was no longer possible to just declare a procedure
or function
without having a class; in my opinion it was a way to discourage people from Procedural Programming which was the old paradigm. But for dead simple data transformation code where you process data from type A to type B you do not have an internal state made of low level building blocks. Therefore, while OOP solved some very important problems it also introduced bureucracy that got imprinted in the brains of a generation that thinks mostyl in classes
as the way for everything.
Good example: database connection
Managing access to a database usually needs maintaining some kind of a connection pool (reusing a number of connections to a database server). It also needs to track if we have already logged in or not, if the remote server is down and so on. But we don't want to know about all that, we just need to know how to configure the DB access and how to run queries. So those functions are the ones that are going to be our interface
: a set of functions and properties that do not change frequently (or at all preferably) while want to keep it from peering eyes how we implemented the connection pool, etc. (making it private
). Moreover, we will have to persist a state
: how's the database access? Did we log in already, is the server up, etc.
// An overly simplified database module
interface Database {
configure: (options: ConfigOptions) => void;
connect: (user: string, password: string) => void;
query<T>: (query: string) => T;
}
class PostgresDatabase implements Database {
// ...
}
// Same public methods but maybe very different concrete implementation!
class MySqlDatabase implements Database {
// ...
}
Bad example: REST handlers
Let's say you have an FAQ page on your company website. Each user comes in and for wants to see an article. Here we maybe have a projection of (userId, articleId, language) => article
. For this a simple function
with its isolated scope would be a great choice as you don't want to persist state between calls. I have a little story. We were new to next.js about 6 years ago and 2 team separately made the same mistake: persisting a state between calls unwittingly. For my team, we managed to serve articles in a random language, depending on the last user's setting - as we were persisting in a local state
the language, it was foolish of course. The other team had it worse: unlike us they didn't catch it in QA and they managed to deploy to prod a bug where users could see other users' private data because of the same general mistake.
So here we don't want to keep a state
between calls and our interface
is also very simple: 1 function with a specific signature (see the projection above). We might need sub-functions that are implementation details but we can hide them neatly by not exporting them from the file.
OOP summary
As I hopefully demonstrated, while OOP is an excellent pattern for certain problems, it is not a magic bullet and can introduce added useless complexity and even be dangerous when used on the wrong thing. A couple of more examples:
- If you build a game, all the enemies are little persisted states with things like
health
, abilities etc. Perfect sense to go with classes! ✅ - If you deal with data structures, components with internal states, complicated algorithms that run for a long time and need to keep tab using a lot of little variables: classes to use! ✅
- Data transformation: you had an API request, you need to simplify that value and pass that onto an analytic platform: you have no internal state, classes are a bad fit! ❌
- Responding to API requests: you might have millions of concurrent requests, each of them should be isolated from each other: having an internal state is actually dangerous here, OOP is ❌
The clash of OOP with the language design of JavaScript
The tension between Java/C# style object-oriented programming and JavaScript lies in the fact that
Javascript (and consequentially TypeScript) is neither strictly functional nor an object oriented language.
(Note: TypeScript is not making JS into an object-oriented language! I heard this baffling myth a couple of times from people.)
It has elements of both but its implementation differs in very important aspects. For example if you used JS for a while you must have encountered curious problems around this
. You passed a method
of a class
to a function
as a callback
and BOOM!