Cool Javascript 9: Named arguments — Functions that get and return Objects

Brought to you by Object destructuring and shorthand property names!

Imagine this piece of code

// some code...apiRequest(
'products',
'GET',
{ category: 3 },
["Content-Type: text/plain"],
function(response) { ... },
null,
true
)
// some more code...

If I were to ask you: “Can you explain me every parameter?” you’d have two options:

Both are suboptimal solutions that require brain power.

I wish we had a way to provide named parameters, similar as what we do in Python or Kotlin, for instance.

Oh wait.

We do have a similar way!

Enter object destructuring and shorthand property names!

(Sounds harder than it is)

We know that this:

var someObject = { a: 1, b: 2, c: 3 }
var { a: a, b: b, c: c } = someObject

will create three variables (a, b, and c) with the values of the object (1, 2, and 3, respectively).

We also know that, given an object key/value pair with the same name, we can remove one of them and achieve a terser syntax:

var someObject = { a: 1, b: 2, c: 3 }
var { a, b, c } = someObject

This would yield exactly the same result as before.

Finally, we know we can pass Objects as function parameters, obviously.

Why not merging this three things we know?

The “timeout: 0” makes *no* sense but I’m literally too lazy to open, edit, save, copy, remove from the post and paste the Gist again ❤.

What happened here? Instead of N parameters, now apiRequest is just expecting a single object. We are immediately destructuring it into some variables, that happen to have the same name as the parameters.

And notice our function call. Now it “explains” what every parameter. Context all over the place!

Isn’t it great?

But wait, there’s more!

Did you know that you can provide default values when destructuring an object?

var someObj = { a: 1, b: 2 }var {
a,
b,
c = 123456,
} = someObj
// a = 1
// b = 2
// c = 123456

We can leverage it to improve our apiRequest function by providing some default values. It’ll help us prevent nasty edge cases and also avoid repetition:

We set some default sensible values. This way we avoid passing some usual values (such as the method or if the request has to be authenticated).

Imagine a authApiRequest that extends apiRequest were we set the token for each request using the default value for the headers attribute:

Notice that we also provide some default value (an empty object) for the whole object (line 9) and also for the getParams parameter (line 4). You should always do this to avoid nasty bugs when trying to access nested properties on undefined objects.

Our function call would look like this now:

apiRequest({
endpoint: 'products',
getParams: { category: 3 },
})

Way cleaner, right?

This pattern is called named parameters (or named arguments), and we can emulate it in Javascript using a plain-old Javascript object plus some ES6 magic and syntax sugar. Neat!

Apart from default values, we can then create specialized versions of our apiRequest function. Some naive examples:

function authApiRequest (params) {
const token = 'Bearer whatever'
return new Promise((resolve, reject) => {
apiRequest(
...params,
{ headers: ...params.headers, Authentication: token }
)
.then(resolve)
.catch(reject)
}
}

or even:

function postRequest (params) {
return new Promise((resolve, reject) => {
apiRequest(...params, method: 'POST')
.then(resolve)
.catch(reject)
}
}

You can use the same pattern with returning values

Our apiRequest return signature could look like this:

function apiRequest({ ... }) {
var response, error, loading
response = error = loading = null
//some amazing code... return {
response,
error,
loading,
}
}

This way, we could destructure the response of our function, providing again context and intent:

var { response, error, loading } = apiRequest({ ... })

This is a nice way of effectively “returning more than one value from a function”. Well, you are actually just returning an Object, but you know what I mean.

You could even use an Array to store and return the values, now that React Hooks kinda made it mainstream.

Recap: benefits of passing and receiving objects as parameters

Warning #1: This is not an excuse to create monstruous function signatures

Ok, we have great tools and we use them to overcome some language shortcomings.

But we should still create small, SRP-compliant functions that receive as few parameters as possible.

Partial application could help you achieve a more readable set of functions.

Rule of thumb: We don’t like Boolean parameters. Usually, a Boolean parameter means two functions inside of one. Robert C. Martin (and Martin Fowler) call this “Flag arguments”.

Contrived example:

function getUsersList ({ includeInactiveUsers = false }) {...}getUsersList()     // what
getUsersList(true) // the fuck

We should aim for something like the following:

function getActiveUsersList() { ... }function getInactiveUsersList() { ... }function getUsersList() {
return [...getActiveUsersList(), ...getInactiveUsersList()]
}

…you might want to create a single function to query the database/whatever to get the filtered user list, instead of doing so in both helper functions. You get the idea.

Warning #2: This is just a pattern

If you create a function called getUserAddressById(?) that only takes a parameter, well, we all know that the parameter is gonna be the ID. Or at least, it should. If it doesn’t, please refer to warning #1.

I mean, don’t rush into your codebase and start dropping objects all over the place. As usual, patterns and models are sometimes useful, but not always.

Use them to clarify the intent of your code. To provide some meaning to the values passed on a function declaration. To provide some default values. Et cetera.

Words matter — Frontend development, CSS, UX, design, lean, agile and everything in between. https://afontcu.dev