DEV Community

Andrea Simone Costa
Andrea Simone Costa

Posted on • Updated on

The shortest way to conditional insert properties into an object literal

Hi, nice to meet you ๐Ÿ˜„!
You can find this article in my personal blog here.

Latest comments (48)

Collapse
 
dubst3pp4 profile image
Marc Hanisch • Edited

I'm a year to late at the party, but thank you very much for the good explanation. I was in search for exactly this feature and it is an elegant and modern solution!

Collapse
 
jfet97 profile image
Andrea Simone Costa

Thanks!

Collapse
 
evandrix profile image
evandrix

~semplicity~ simplicity

Collapse
 
charon92 profile image
Tom Pegler

For added fun with this, I tried a couple of things:

var state = 'tom';
var o = {
  name: 'test',
  state: state,
  ...( ( true && ( this.state = 'jamie' ) ) && false ) && { state } 

}
Collapse
 
jefflindholm profile image
Jeff Lindholm

Personally, I would do it like ...(obj && { props: foo}) so it is obvious how the operators work, people who don't immediately know that && has precedence over ...won't read the code as easily.

Collapse
 
davidsharp profile image
David Sharp

This seems much nicer than my current const obj = { ...{ condition? { prop: value }: {} }}; ๐Ÿค”

While it's not entirely clear at first glance how it works, I think the intent is clear enough for someone stumbling across it (excusing any bugs caused by gotchas)

Collapse
 
ctimmerman profile image
Cees Timmerman

I know how && works, but wtf does ... do?

Collapse
 
jfet97 profile image
Andrea Simone Costa

Google rest/spread operators ๐Ÿ˜†

Collapse
 
ctimmerman profile image
Cees Timmerman • Edited

It unpacks stuff, like * in Python, which "spread" could be a synonym for, but "rest operator"? googles Ah, as in "remaining parameters".

Collapse
 
chadkirby profile image
Chad Kirby

Mind blown!

Collapse
 
stanlindsey profile image
Stan Lindsey

Basically, it evaluates like this:
...(condition && { prop: value }),

Collapse
 
carlospaz2084 profile image
Carlos

Very helpful what Stan posted here. Parenthesis really help. Actually they are a default lint recommendation in this specific case. I think adding them to the code in the article would help a lot.

Collapse
 
jfet97 profile image
Andrea Simone Costa

But it wouldn't be the shortest anymore ;)

Thread Thread
 
carlospaz2084 profile image
Carlos

hey man! I'm trying to help you here! lol...great article!

Collapse
 
danderson profile image
Dale Anderson

It's a useful trick! There are a bunch of others at blog.bitsrc.io/6-tricks-with-resti... (not my post), including a concise way to remove properties from objects.

Collapse
 
konrud profile image
Konstantin Rouda

Interesting trick, but what if our value is 0 which can be a valid value to use? So our code:

๐ฏ๐š๐ซ ๐œ๐จ๐ข๐ง๐ฌ = ๐ŸŽ;
๐ฏ๐š๐ซ ๐จ๐›๐ฃ = {
  ๐ง๐š๐ฆ๐ž: "๐‰๐จ๐ก๐ง",
  ...๐œ๐จ๐ข๐ง๐ฌ && { ๐œ๐จ๐ข๐ง๐ฌ๐€๐ฆ๐จ๐ฎ๐ง๐ญ: ๐œ๐จ๐ข๐ง๐ฌ }
}
Enter fullscreen mode Exit fullscreen mode

won't copy anything.

Collapse
 
kaptenadhoc profile image

Just make sure to internalize what JavaScript considers truthy and you won't think this is a "gotcha" anymore.

So in your specific case you'd probably want to do something like

const coins = 0;
const obj = {
  name: "John",
  ...typeof coins === "number" && !isNaN(coins) && {coins},
};
Enter fullscreen mode Exit fullscreen mode
Collapse
 
konrud profile image
Konstantin Rouda

AFAIK, this is considered as a bad practice to use typeof to check the number, as NaN is also has type number (e.g. typeof NaN === "number"; // -> true). Moreover, typeof when used against number will return number, as a result, in lower case (i.e. not Number but number).

Thread Thread
 
kaptenadhoc profile image
Reply guy ๐Ÿคทโ€โ™‚๏ธ

Yeah the casing of number was just a typo. Adjusted it to check for NaN.

Collapse
 
tkane2000 profile image
tkane2000

you could pass coins to a method that checks for false, null, and undefined only (or something along those lines)

Collapse
 
jfet97 profile image
Andrea Simone Costa • Edited

So there is no point to use the trick in this way if 0 is a valid amount as well.

Remember that on the left you put the condition, so you may write like the following:

๐ฏ๐š๐ซ ๐œ๐จ๐ข๐ง๐ฌ = ๐ŸŽ;
๐ฏ๐š๐ซ ๐จ๐›๐ฃ = {
  ๐ง๐š๐ฆ๐ž: "๐‰๐จ๐ก๐ง",
  ...๐œ๐จ๐ข๐ง๐ฌ>=0 && { ๐œ๐จ๐ข๐ง๐ฌ๐€๐ฆ๐จ๐ฎ๐ง๐ญ: ๐œ๐จ๐ข๐ง๐ฌ }
}
Collapse
 
azinod profile image
Bruno Donizetti • Edited

This makes total sense. I usually just build the object then run a sanitize function on it. No wonder why I never stoped to think about this.

Thanks for posting, simple trick but mind opening (mainly if you are biased by different approaches like me).

Collapse
 
jfet97 profile image
Andrea Simone Costa

I do love find alternative ways to do common things ๐Ÿ˜†, I'm honored to have opened your mind ๐Ÿ˜

Collapse
 
klikas profile image
theodoros klikas • Edited

I believe in a quote, attributed to Brian Kernigan that goes like:

"Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?"

In languages like javascript, there is a minifier anyway (and a transpiler most probably) in front of our code and the production code that gets served in the browser (unless of course your write code for the server-side).

To be fair, the above is not the worst example of "smart" code that I have seen and the article is a nice explanation of why this works so thanks for that!

Collapse
 
kaptenadhoc profile image
Reply guy ๐Ÿคทโ€โ™‚๏ธ

To me, more terse code is almost always easier to parse. Of course, everyone's different. There's not really a one size fits all solution. You'll have to agree with your team on a working standard.

Collapse
 
jfet97 profile image
Andrea Simone Costa

I've already expressed mine opinion here around, and to be technically precise the code is easily testable as any if construct.
Thanks for sharing your opinion!

Collapse
 
klikas profile image
theodoros klikas

I do find the article helpful but to be honest with you, it was the first time I saw something like this.

I guess someone that is more experienced with the spread operator and augmenting objects in this way might have seen it more than me.

As you said, it is most probably a matter of opinion as a lot of things in javascript are those days, for example, I find something like this pretty hard to read:

let adder = (x) => (y, z) => x + y + z;

compared to the old-style alternative but there are people who love it.

So thank you again for going deep in your explanation :)

Thread Thread
 
jfet97 profile image
Andrea Simone Costa

You're welcome!

Collapse
 
willsmart profile image
willsmart • Edited

That's awesome. Thanks for posting.
I didn't know that the rest operator could take an expression (though it does make sense).

I think your example could be better though. As said you'd quickly run into unexpected behaviour with falsey primitive values, which you mention but downplay. It's a showstopper imo.
As a shortened, silly example showing what could happily ruin your day in longer, serious code...

wrappedStringLength  =  string => ({...string&&{string}}.string.length)
wrappedStringLength('a')
>> 1
wrappedStringLength('')
>> Uncaught TypeError: Cannot read property 'length' of undefined

Most coders would assume a method would treat an empty string like any other.

I'd just code the example object as...

{
    state, priority,
    collection: 'Cats',  
    sort: 'asc',
}

Just simpler and less brittle.

A better example might use something where the inserted properties are computed. a la...

userObject  =  ({id, name, type, age}) => ({
  name, age, type,
  ... type=='prof' && { gradStudents: fetchGradStudentsForProf(id)  }
  ... type=='roofer' && { jobs: fetchJobsByRoofer(id) }
})
Collapse
 
jfet97 profile image
Andrea Simone Costa

I think you will understand that this is beyond the scope of the article.
My first point was to show a nice, little know js fact. The second was to explain why such code is allowed.
All the rest is left to the reader's good will and curiosity :D

Collapse
 
felixfbecker profile image
Felix Becker

In most cases, just defaulting the property value to undefined is the easier choice.

Collapse
 
bobmyers profile image
Bob Myers

In the case of Firebase, to take one example, a value of undefined is treated differently than a missing property, and causes an error.

Collapse
 
guico33 profile image
guico33

Interesting article, I had always found that behavior a bit eery and never bothered looking into the why. Thanks for sharing ๐Ÿ™

Collapse
 
jfet97 profile image
Andrea Simone Costa

You're welcome!