15

Was updating a bit of code today that returns optional props to a React component. I discovered that even though the function sometimes returns null, it wouldn't error when the return value was immediately unpacked.

Pedantic summary of the code:

const returnPropsOrDont = (condition) => {
  if (condition) return null;
  return { optionalProp: 'foo' };
}
...
render() {
  const { condition } = props;
  return <SomeComponent
           staticProp="staticProp"
           {...returnPropsOrDont(condition)}
         />
}

Upon realizing this was cool, I ran to the console and tried it out on Objects and Arrays. Alas -

   > {...null} // {}
   > {...undefined} // {}, which is interesting because null is an object but undefined is not
   > [...null] // Uncaught TypeError: object null is not iterable

I did some light Googling and found one article which suggests that TypeScript considers it a feature to make sure optionally defined values don't haunt an unsuspecting developer. Fine, but a) I'm not using TypeScript, and b) I have no idea why JS wouldn't guard Arrays in the same way.

Since this does seem like a guard against optionally defined values, why is {...null} fine and [...null] not?

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
Stick
  • 253
  • 1
  • 2
  • 7

2 Answers2

25

Normally, uses of ...x requires x to be iterable because the point of ... is normally to flatten an iterable into its components. An array is a prime example of an iterable.

null is not iterable. It has no components, so it doesn't make sense to iterate over null. for (const e of null) will similarly fail, since of also requires an iterable.

However, {...x} requires x to be enumerable because it needs not just values, but keys along with them. An object is a prime example of an enumerable.

null is enumerable. null is sometime treated as an object, and this is one of those cases. Objects are enumerable because they can have properties. for (const p in null) will similarly succeed, since in requires an enumerable.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Yeah, I think I was misled by the seemingly overloaded `...` operator and the subsequent article I found didn't help by declaring it a 'feature', making the use with an Array look like an oversight. – Stick Jul 13 '20 at 22:52
  • 1
    `null` is not an object. It does not have properties (not even an empty list of properties). Try `Object.getOwnPropertyDescriptors(null)`. – Felix Kling Jul 13 '20 at 22:52
  • @ikegami: Yes, [but `typeof` is weird](https://www.ecma-international.org/ecma-262/10.0/#table-35b). [`null` is its own data type](https://www.ecma-international.org/ecma-262/10.0/#sec-ecmascript-language-types-null-type). – Felix Kling Jul 13 '20 at 22:54
  • @Felix King, Ok, I tweaked the wording. – ikegami Jul 13 '20 at 22:58
2

[…null] - here you use spear operator and it works only for iterable objects (like strings, arrays, generators or custom iterable objects), but null isn't iterable object and will recieved error. You can create your own iterable object be adding special property Symbol. iterator

{...null} - works because typeof null == 'object', but in real life it doesn't make sense

Slawa Eremin
  • 5,264
  • 18
  • 28
  • Why should it work with `undefined` then? `{...undefined}` yields an empty object as well, afaik `typeof undefined == 'undefined'` – Stick Jul 13 '20 at 22:40
  • 1
    https://stackoverflow.com/questions/47155141/spreading-undefined-in-array-vs-object – Slawa Eremin Jul 13 '20 at 22:44
  • So really the problem is that `...` is contextually different. Perhaps the article was misleading and it's not a special feature, just JS being JS – Stick Jul 13 '20 at 22:48