To add to the already provided answers, spread is not an operator (as you've mentioned in your question), but rather, it is a syntax. This matters because ...x
doesn't produce a value like operators such as +
and -
do, and it can't be used standalone - it needs to be used in certain contexts, such as in arrays [...x]
or objects {...x}
. Andrew Li goes into great detail about this in his great answer.
So while they look the same, the behaviour and runtime semantics of ...x
depends on the context (ie: where it's being used). In fact, as Nick rightly points out in their answer, object spread/spread properties {...x}
and array spread [...x]
were added in two completely separate versions of the ECMAScript (JS) specification (spread properties being introduced in ECMAScript2018 and array spread being introduced in ECMAScript2015, aka ES6).
Spread properties {...x}
:
When we use the spread syntax in an object literal {...x}
, the requirement is that the expression to the right-hand side of the ...
(ie: x
) evaluates to an object or to something that can be converted to an object (with exceptions for null
and undefined
). The (enumerable own) properties of this object are then copied into the object literal {}
we're creating. When you end up doing:
{...false}
JavaScript sees that false
is not an object (it's a primitive), so it converts it to one by wrapping it in a boolean object:
{...new Boolean(false))}
Since new Boolean(false)
doesn't have any own-enumerable properties, the new object we're creating {}
doesn't get any properties copied into it, so you end up with an empty object.
Array spread [...x]
:
Using ...x
(spread element) in the context of an array [...x]
runs a different algorithm for spreading behavior compared to the one described above for spread properties {...x}
.
With array spread, the requirement is that the expression to the right-hand side of the ...
evaluates to something iterable.
In this context, something is iterable if it's an object that has the property/well-known symbol of Symbol.iterator
defined on it that when invoked, returns an iterator. Arrays natively have this symbol which is why we can use ...[]
on them in the context of arrays, hence why your second example works with true && ['bar']
. Each value that the iterator produces is added to the newly created array that the spread syntax is being used in.
When x
in [...x]
evaluates to something non-iterable such as false
in your example, JS is unable to iterate it, and so you get a TypeError.