DEV Community

loading...

JS Clean Code tip: Don't use tuples for returned objects

András Tóth
A developer with M.Sc. in Computer Science. Working professionally since 2010. In my free time I make music and sometimes cook. Also I don't have Twitter. Sry!
・1 min read

I am not going to talk about the Record & Tuple Proposal, but about this bad habit:

function createSomething() {
  const thing1 = { 
    aProperty: 'indeed'
  };

  function thing2() { /* ... */ }

  return [thing1, thing2];
}
Enter fullscreen mode Exit fullscreen mode

There is absolutely everything wrong with this example. Honestly I was kinda surprised to see it in actual production code.

The problem is losing naming

So let's see some examples of using the thing above:

// Having a typo won't cause any errors
// but you won't find the usages.
const [thang, thing2] = createSomething();

// This is not just valid, but super misleading!
// You accidentally changed the order and you had no errors!
const [thing2, thing1] = createSomething();
Enter fullscreen mode Exit fullscreen mode

We are losing the meaningful names still intact inside createSomething with nothing in return. (React team uses this actually to name your hooks in any way you want - but they also try to enforce naming through linter rules).

How to fix it

Simply return an object:

return { thing1, thing2 };
Enter fullscreen mode Exit fullscreen mode

And this is how you use it:

// will cause error
const { thang, thing2 } = createSomething();

// order does not matter, no penalty 
// if you misremember the order
const { thing2, thing1 } = createSomething();
Enter fullscreen mode Exit fullscreen mode

💪 🦾 💪 🦾 💪 🦾 💪 🦾

Discussion (2)

Collapse
ronnewcomb profile image
Ron Newcomb • Edited

Counterpoint: call the same function multiple times. Without tuples, now you have name clashes:

const MyComponent = () => {
    const { data: customers, error: custError } = useAsync(getCustomers);
    const { data: order, error: ordError } = useAsync(getOrders);
    const { data: invoices, error: invError } = useAsync(getInvoices);
Enter fullscreen mode Exit fullscreen mode

Returning tuples makes this issue go away. This is why React uses tuples for many hooks.

Collapse
latobibor profile image
András Tóth Author

This is a very valid, but a very rare use case. My point above is that whenever you use tuples you open a class of problems about "magic location". So you have to use them very sparingly with a lot of consciousness.

Here you also demonstrate that it is highly unlikely that const { data: customerError, error: customers } = useAsync(getCustomers) would happen, since it would look odd and you will not fail it.

Lastly in the example above, why don't use transform data inside getCustomers already so it returns {customers, customerError} instead? What if you realize you need to return more than two objects?

So, my point is: use it very rarely and very carefully.