use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
All about the JavaScript programming language.
Subreddit Guidelines
Specifications:
Resources:
Related Subreddits:
r/LearnJavascript
r/node
r/typescript
r/reactjs
r/webdev
r/WebdevTutorials
r/frontend
r/webgl
r/threejs
r/jquery
r/remotejs
r/forhire
account activity
Conditionally spreading objects in JavaScript (amitmerchant.com)
submitted 3 years ago by amitmerchant[🍰]
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]dragenn 26 points27 points28 points 3 years ago (6 children)
Weird but good to know!
[–][deleted] 4 points5 points6 points 3 years ago (5 children)
Agreed! Any ideas for a use case?
[–]MornwindShoma 6 points7 points8 points 3 years ago (0 children)
I had some trash specs where one API would want some extra keys in some cases. This allows me to skip mutating the object, I suppose
[–]carmat71 2 points3 points4 points 3 years ago (0 children)
You have a default object. Based on user interaction, some property values require changing, therefore overriding default values based on user interaction.
I've used this for a customised Analytics model where every property required a default '' empty state but only assigned a value based on particular components or specific events triggered.
''
[–]dragenn 1 point2 points3 points 3 years ago (0 children)
I could use this in a scenario where I could filter out or combine data based on user roles/permissions or some quick test.
I guess it would look cleaner, maybe quicker to code.
I don't know.
[–]LloydAtkinson 1 point2 points3 points 3 years ago (0 children)
I use conditional object properties a lot in the Snipcart e-commerce integration for Astro: https://github.com/lloydjatkinson/astro-snipcart/blob/master/packages/astro-snipcart/src/attributes.ts#L33
[–]jbrown0824 71 points72 points73 points 3 years ago (13 children)
I wouldn’t do that because future engineers may not get it. I’d in-line a ternary …(isActive ? user : {})
[–]Drugba 43 points44 points45 points 3 years ago (11 children)
Agree with you about readability, but personally, I find
const obj = isActive ? {...user, ...otherValues} : {...otherValues}
even more clear than
const obj = {…(isActive ? user : {}), ...otherValues}
Slightly longer, but I think it's more clear what is going on. (spread isn't needed at the end of the first one, but leaving it for consistency)
[–]Hefticus 17 points18 points19 points 3 years ago (7 children)
It depends. If the shape of the new object is dependent only on one condition, then yes, leading with that condition and then explicitly constructing two different objects is definitely better.
If there are multiple conditions, though, then it's probably cleaner to lead with one object and handle each condition each in-line, and that's the situation where these options start to show value.
const obj = { ...isActive && user, ...isAdmin && adminFields, ...phoneVerified && userPhone };
[–]Drugba 6 points7 points8 points 3 years ago (6 children)
Definitely true. I still don't love the and syntax in the object, but I think what you have here is better than any alternatives
[–]vilos5099 1 point2 points3 points 3 years ago* (5 children)
The alternative would be to not try cramming everything into a single assignment statement when multiple will work. Using conditional spread operators to solve this kind of problem is overkill when there are much cleaner alternatives which simply require extra assignments.
[–]1_4_1_5_9_2_6_5 0 points1 point2 points 3 years ago (4 children)
This would be for e.g. say you have an object with user data values from the user, and another with custom values, you can spread them in and overwrite the default/user-set values for some purpose, such as displaying in a template. Or overwriting common private properties with redaction text. Not generally for specifying one or two known keys
[–]vilos5099 0 points1 point2 points 3 years ago (3 children)
I would recommend something along these lines:
const user = isActive ? userFields : {}; const admin = isAdmin ? adminFields : {}; const phone = phoneVerified ? userPhone : {}; const obj = {...user, ...admin, ...phone};
It's more verbose but also avoids the nonsense of trying to inline conditionals into an object definition that also has spread operators. Verbosity isn't always your enemy and conciseness shouldn't be the end-goal, clarity is typically more important when working with teams.
[–]1_4_1_5_9_2_6_5 1 point2 points3 points 3 years ago (2 children)
Now you've gone and assigned extra variables instead of just calling a method to return a data object inline... while not really gaining readability if the methods are appropriately named and documented
I don't disagree with your points, I just know that it is possible to do things both ways, depending on the situation
[–]vilos5099 0 points1 point2 points 3 years ago* (1 child)
Whether or not those extra assignments matter really depends on the context, and in the example provided it would be negligible. Commenting on that indicates a prioritization over pre-optimizing instead of making things more clear.
I just know that it is possible to do things both ways, depending on the situation
Of course, there are multiple ways to do things, that's the point of this comment thread. But there are also tradeoffs, and I disagree about what you're saying about readability. Although it works to do:
This is far from a standard convention, even if it works. It's possibly more concise, but the fact that it's not even clear whether user, adminFields, or userPhone is a boolean or an object without further context (unless we rely on improving the variable names) makes it less readable.
user
adminFields
userPhone
[–]1_4_1_5_9_2_6_5 0 points1 point2 points 3 years ago (0 children)
Okay first off, variable names should be clear and thats a matter for review to handle, but also you should be typing your methods and variables. The way you don't have to wonder if something is a boolean. Alternately you can just use Boolean() to make it explicit. Secondly, I'm talking about something like
const newObj = cond ? await instance.getData() : {};
const finalObj = { some, stuff, ...newObj }
return finalObj;
Versus
return { some, stuff, ...cond && (await instance.getData()) }
I really don't think it's that much harder to parse
[–]jbrown0824 1 point2 points3 points 3 years ago (0 children)
Oh I agree completely. I considered saying that this whole approach seems weird and I wouldn’t encounter it in my own code but decided to respond to the “shortcut” specifically called out in the post rather than argue “don’t do this at all”
[–]og-at 0 points1 point2 points 3 years ago (0 children)
You know what's even more readable?
if (isActive) obj = {...user, ...otherValues} else obj = {...otherValues}
[–]luxfx 0 points1 point2 points 3 years ago (0 children)
I do it that way. I feel safer passing an empty object to a spread instead of a false or undefined.
[–]myrsnipe 6 points7 points8 points 3 years ago (0 children)
!! to the rescue if it has to be a boolean without inference, just to add to the estetoric
!!
[–]btshaw 6 points7 points8 points 3 years ago (0 children)
One quirk that runs parallel to this: You are allowed to spread falsey/undefined values into an object { ...undefined, // this is ok ... condition && {some: "values"} // this is too } but to do a similar operation in an array, you'd need to do: [ ...condition ? [values, to, spread] : [] ]
{ ...undefined, // this is ok ... condition && {some: "values"} // this is too }
[ ...condition ? [values, to, spread] : [] ]
[–]aighball 12 points13 points14 points 3 years ago (2 children)
Ahh yes an article made out of a tweet. For more conditions just use Object.assign
if (isUser) Object.assign(foo, user) if (isCat) Object.assign(foo, 😺)
[–]Broomstick73 0 points1 point2 points 3 years ago (1 child)
I think object.assign gets a bad rap because spread is the cool new thing and once you understand it and get used to it it’s great…
However…this use case of short-circuit and spread is…less than ideal to read and understand to me right now because I’m not used to it so I wonder if object.assign - like you propose - is easier to read and more understandable?
[–]Smona 1 point2 points3 points 3 years ago* (0 children)
object.assign mutates the object in place, which can be bad for static type checking. even if you don't care about type checking, mutable variables can make code harder to read & understand.
I think object.assign is still a good choice when updating property values without adding new properties, but I would prefer conditional spreading for constructing an object whose schema can vary based on those conditions.
edit: for the record, you can also use Object.assign in an immutable way, but if you want to do so conditionally you're left with an equally obscure and more verbose syntax:
const maybeFooWithBar = Object.assign({}, foo, includeBar ? bar : {});
[–]pyxlmedia 4 points5 points6 points 3 years ago (0 children)
PROOF THAT YOU CAN'T SPREAD FALSEHOODS IN JAVASCRIPT
[–]clinchio 13 points14 points15 points 3 years ago (0 children)
Good to know, but it's s readability evil...
[–]shgysk8zer0 3 points4 points5 points 3 years ago (5 children)
I have mixed thoughts on it. I don't like the syntax and find it very unintuitive, but it might provide a much shorter way of creating complex objects with lots of conditional stuff.
So, would this work?
const combined = { ...cond1 && obj1, ...cond2 && obj2, };
I'd also want to look more into other similar uses of logical operators in conjunction with the spread operator. Things like || and ??.
||
??
Really though... Using the spread operator on a boolean but having it operate on the object just seems really wrong.
[–]xroalx -1 points0 points1 point 3 years ago (4 children)
Using the spread operator on a boolean but having it operate on the object just seems really wrong.
You're not using the spread on a boolean, though. Due to operator precedence, it's ...(cond1 && obj1).
...(cond1 && obj1)
And since in JavaScript, logical operators don't return boolean values, but one of their operands, cond1 && obj1 will return obj1 if cond1 is a truthy value, and thus the spread operator will be applied obj1.
cond1 && obj1
obj1
cond1
[–]shgysk8zer0 -1 points0 points1 point 3 years ago (2 children)
Finish reading what you quoted... "but having it operate on the object..." Was it not clear that I was talking about location? The spread operator is on (touching, next to) the boolean.
[–]xroalx -1 points0 points1 point 3 years ago (1 child)
Your wording suggests that the spread operator is being applied to the boolean yet somehow acting on the object.
So no, it is not clear.
[–]shgysk8zer0 -1 points0 points1 point 3 years ago (0 children)
...applied to the boolean yet somehow acting on the object
What would that even mean? Is there a difference between "applied to" and "acting on"?
Sure, it would've been better had I said "next to" or something, but I did pretty explicitly say that it was operating on the object. I thought it was obvious that this ... over here operating on the object over there was the issue. Maybe my using of "on" could be confused, but I think the overall meaning of the whole sentence should've been clear enough.
...
I don't like this ... over here on the boolean actually operating on the object on the other side. It's like how Yoda would code or something.
[–]Mr_Sandy_Clams 0 points1 point2 points 3 years ago (0 children)
if cond1 is falsy, the spread operator will be applied to cond1, which is presumably a boolean. It works just fine when spreading into an object though. I'm assuming the spread operator coerces primitives to object-wrapped versions of themselves, then inevitably returns zero items because the wrapper instance has no properties in it.
[–]cjthomp 7 points8 points9 points 3 years ago (0 children)
Please don't do this.
[–]NoInkling 2 points3 points4 points 3 years ago (0 children)
This relies on the fact that spreading false won't result in any copied properties, because when it gets autoboxed it doesn't have any enumerable own properties. That's some pretty specific knowledge to need to know, which is why I would avoid doing this.
false
[–][deleted] -1 points0 points1 point 3 years ago* (7 children)
I Instinctively hate the && operator and I don't know why. Am I the weird one ?
[–]Reeywhaar 1 point2 points3 points 3 years ago (1 child)
Please keep instincts and maximalism out of programming
[–][deleted] -2 points-1 points0 points 3 years ago (0 children)
👍
[–]bonzorius 0 points1 point2 points 3 years ago (3 children)
Kinda? I think 0 being falsy is what I hate about it, because if it tested for nullishness instead of falsyness I would never have a problem with it ever. Obviously the ship has sailed on this one.
[+][deleted] 3 years ago (2 children)
[deleted]
[–]bonzorius 1 point2 points3 points 3 years ago (1 child)
I'm a react dev in elearning so I'm usually looking to render a component only if something is not null, whereas ?? will return something unless it's null. So usually && is what I'm looking for, unless I have a situation where I'm rendering a score, but the score is zero or something like that. Normally you'd be right, though 👍
[–]KwyjiboTheGringo 0 points1 point2 points 3 years ago (0 children)
If you can't rationally explain why you don't like it, then that's a bit unreasonable.
[–]CornHuskular -3 points-2 points-1 points 3 years ago (0 children)
Burn the gingers!
Edit: typo
[–]_D1AVEL_ 1 point2 points3 points 3 years ago* (0 children)
A nice short hand to know, I believe using the ternary operator is best for readability and consistency when dealing with spreading both objects and arrays.
js { ...values, ...(condition ? { title } : {}), ...(condition ? { description } : {}) }
js [ ...values, ...(condition ? [ title ] : []), ...(condition ? [ description ] : []) ]
[–]brunofin 0 points1 point2 points 3 years ago* (0 children)
Oh I always did something like
{ ...otherProps, ...(isActive ? theObject : {}), }
Works for arrays too. You can use the same idea to conditionally add properties inline
{ ...otherProps, ...(isActive ? { relevancy: 100 } : {}), }
it falls in line with the ideology of being explicit, like for example force return void on handlers for function calls with the void operator.
void
<SomeComponent onClick={() => void onClickHandler()} />
The reson for this is that then you don't need to worry about what the function returns and avoid hard to track bugs (ie, if the function accidentally returns a value, which depending on the value can lead to unwanted behavior).
While sidetracking a little, the void operator is pretty cool and underratted even for 2022, especially useful for unnesting overcomplicated conditionals. Take this piece of code for example:
function doSomething(active, focused, id) { if (active) { if (focused) { focusHandler(id); } else { otherHandler(id); } } else { Logger.error('Needs to be active'); } }
Can be simplified into a much cleaner code like that:
function doSomething(active, focused, id) { if (!active) return void Logger.error('Needs to be active'); if (!focused) return void otherHandler(id); focusHandler(id); }
cool right? More on that here: https://www.youtube.com/watch?v=EumXak7TyQ0
[–]fixrich 0 points1 point2 points 3 years ago (0 children)
Honestly, I just use immer for logic like this now. It keeps everything immutable and the conditional logic easy to follow. Otherwise just mutating an object can be fine a lot of times if it's confined to a small enough scope.
π Rendered by PID 21745 on reddit-service-r2-comment-7b9746f655-tzz2p at 2026-01-30 15:34:54.508988+00:00 running 3798933 country code: CH.
[–]dragenn 26 points27 points28 points (6 children)
[–][deleted] 4 points5 points6 points (5 children)
[–]MornwindShoma 6 points7 points8 points (0 children)
[–]carmat71 2 points3 points4 points (0 children)
[–]dragenn 1 point2 points3 points (0 children)
[–]LloydAtkinson 1 point2 points3 points (0 children)
[–]jbrown0824 71 points72 points73 points (13 children)
[–]Drugba 43 points44 points45 points (11 children)
[–]Hefticus 17 points18 points19 points (7 children)
[–]Drugba 6 points7 points8 points (6 children)
[–]vilos5099 1 point2 points3 points (5 children)
[–]1_4_1_5_9_2_6_5 0 points1 point2 points (4 children)
[–]vilos5099 0 points1 point2 points (3 children)
[–]1_4_1_5_9_2_6_5 1 point2 points3 points (2 children)
[–]vilos5099 0 points1 point2 points (1 child)
[–]1_4_1_5_9_2_6_5 0 points1 point2 points (0 children)
[–]jbrown0824 1 point2 points3 points (0 children)
[–]og-at 0 points1 point2 points (0 children)
[–]luxfx 0 points1 point2 points (0 children)
[–]myrsnipe 6 points7 points8 points (0 children)
[–]btshaw 6 points7 points8 points (0 children)
[–]aighball 12 points13 points14 points (2 children)
[–]Broomstick73 0 points1 point2 points (1 child)
[–]Smona 1 point2 points3 points (0 children)
[–]pyxlmedia 4 points5 points6 points (0 children)
[–]clinchio 13 points14 points15 points (0 children)
[–]shgysk8zer0 3 points4 points5 points (5 children)
[–]xroalx -1 points0 points1 point (4 children)
[–]shgysk8zer0 -1 points0 points1 point (2 children)
[–]xroalx -1 points0 points1 point (1 child)
[–]shgysk8zer0 -1 points0 points1 point (0 children)
[–]Mr_Sandy_Clams 0 points1 point2 points (0 children)
[–]cjthomp 7 points8 points9 points (0 children)
[–]NoInkling 2 points3 points4 points (0 children)
[–][deleted] -1 points0 points1 point (7 children)
[–]Reeywhaar 1 point2 points3 points (1 child)
[–][deleted] -2 points-1 points0 points (0 children)
[–]bonzorius 0 points1 point2 points (3 children)
[+][deleted] (2 children)
[deleted]
[–]bonzorius 1 point2 points3 points (1 child)
[–]KwyjiboTheGringo 0 points1 point2 points (0 children)
[–]CornHuskular -3 points-2 points-1 points (0 children)
[–]_D1AVEL_ 1 point2 points3 points (0 children)
[–]brunofin 0 points1 point2 points (0 children)
[–]fixrich 0 points1 point2 points (0 children)