amitmerchant1990 / amitmerchant-dot-com-comments

1 stars 0 forks source link

conditionally-spreading-objects-in-javascript/ #60

Open utterances-bot opened 2 years ago

utterances-bot commented 2 years ago

Conditionally spreading objects in JavaScript — Amit Merchant — A blog on PHP, JavaScript, and more

In JavaScript, if you want to populate an object with some properties from another object, you can use the spread operator (...) to do so.

https://www.amitmerchant.com/conditionally-spreading-objects-in-javascript/

zachlysobey commented 2 years ago

FWIW it seems like its not only booleans that work

f = { ...null && { a:1 } }
{}
{ ...true && { a:1 } }
{a: 1}
{ ..."" && { a:1 } }
{}

Also, a matter of style, but maybe good to wrap the boolean expressions in parens to reduce opportunity for confusion.

const activeUsers = {
  ...(isActive && user)
};
stefanvonderkrone commented 2 years ago

FWIW it seems like its not only booleans that work

f = { ...null && { a:1 } }
{}
{ ...true && { a:1 } }
{a: 1}
{ ..."" && { a:1 } }
{}

Also, a matter of style, but maybe good to wrap the boolean expressions in parens to reduce opportunity for confusion.

const activeUsers = {
  ...(isActive && user)
};

Have you tried a non empty string or any non primitive value?

SirWrexes commented 2 years ago

A non empty string does act as a truthy value, and the object gets spread. As for non-primitives, even if an object's valueOf returns false, the object itself being a non-null reference takes precedence and is truthy.

const obj = {
  a: 23,
  b: '42',
};

const spread1 = {
  ...('Will it blend ?' && obj),
};

console.log('spread1: ' + JSON.stringify(spread1));

// ---  //

const nonPrimitive = {
  valueOf: () => false,
};

console.log(
  'Non primitive truthiness check :\n',
  '\t- nonPrimitive == false ->  ' + (nonPrimitive == false) + '\n',
  '\t- nonPrimitive === false -> ' + (nonPrimitive === false) + '\n',
  '\t- nonPrimitive && true   -> ' + (nonPrimitive && true)
);

const spread2 = {
  ...(nonPrimitive && obj),
};

console.log('spread2: ' + JSON.stringify(spread2));

Basically it works like any other test when it comes to determining truthiness, and as this MDN page mentions

In JavaScript, a truthy value is a value that is considered true when encountered in a Boolean context. All values are truthy unless they are defined as falsy. That is, all values are truthy except false, 0, -0, 0n, "", null, undefined, and NaN.

As a matter of fact, even empty objects {} and empty arrays [] are non null references and evaluate to true.

IsTheJack commented 2 years ago

Very nice tip. Just for fun purposes you can use async await inside these "spreadable expressions" as well:

const asyncValid = Promise.resolve(true)
const asyncInvalid = Promise.resolve(false)

async function asyncOperation() {
  const valid = {
    foo: 'valid'
  }

  const invalid = {
    bar: 'invalid'
  }

  const someObject = {
    ...(await asyncValid && valid),
    ...(await asyncInvalid && invalid)
  }

  console.log({ someObject })
}

asyncOperation()

I would not do that, but it's always awesome to discover straight ways to do something...

stefanvonderkrone commented 2 years ago

This is so cool!

aantipov commented 2 years ago

The catch here is that in the {...val} expression

  1. Javascript engine evaluates val as an object and makes the necessary coersion if it's not an object. String values are evaluated as String objects, false/true as Boolean objects. Null is an object on its own
  2. Spread operator ... enumerates the own properties of the object. String, Boolean, null objects do not have own properties, hence their destructuring is equivalent to destructuring of an empty object {...{}}