Ever hear of ‘pure functions’ in Javascript – and Typescript by extension – and wonder what they are?
Pure functions, can simply be described as functions that do not modify their inputs or create side effects.
This is important because in Javascript where everything is dynamic by default, it is easy to completely change the value of something, intentionally or unintentionally, which can cause unpredictable behaviours.
Let’s have a look at what that means.
Here’s a simple pure function, we’ll be using Typescript:
function generateToken(userId: string): Promise<string> {
const user = await userService.find(userId);
if (!user) throw new Error("USER_NOT_FOUND");
const token = jwt.sign(user, SOME_SECRET);
return token
}
In this fairly contrived example, we pass in a userId, find that user and if found, return a signed JWT token, which has all the details of the user.
The thing that makes this function pure is two fold:
- We do not manipulate the input
userId
in any way - We are not changing anything except generating the JWT token.
Typescript actually makes it a lot easier to write pure functions because it prevents us from manipulating function inputs by default, so if we attempted to change userId
, our IDE would let us know that that is probably a bad idea.
So we only really gotta worry about the second part!
So let’s take a look at a function that might not be pure, and for the fun of it, let’s user the contrived “find” method from the userservice:
function find(userId: string) {
const user = await some-query-language-or-ORM-method;
const now = new Date();
user.updatedAt = now;
return user
}
While this is yet another contrived example, it’s fairly straightforward what it does. It finds the user, using an imaginary database query and returns it. However, before returning it does something interesting: It updates the users updatedAt
attribute.
While this may seem perfectly innocent and in fact just seem like a help, that the caller does not have to worry about doing this, it is actually considered a side effect.
Side effects are bad because by definition they are unexpected behaviour that changes or does things we weren’t planning on.
In this case, setting an updatedAt
attribute probably doesn’t do any harm, but what if the property was password
? Yeah that doesn’t sound great.
Alternatively, what if at a later point we wanted to implement a lastLoggedIn(userId: string)
method, that returns the updatedAt
for a given user?
This would start having weird “bugs” because everything we query a user in the database, they appear to be logged in, when in fact perhaps we were just looking up that user for something else entirely.
I hope it’s clear what I’m getting at here:
Code that is not directly related to the expected behaviour generate so called side effects, and we want to avoid that at all costs.