As noted in the comments, and summarized by @ftor from #687, object spread is equivalent1 to Object.assign() (issues #687, #45), whereas spread in array literal context is iterable spread.
Quoting Ecma-262 6.0, Object.assign() is defined as:
19.1.2.1 Object.assign ( target, …sources )
The assign function is used to copy the values of all of the enumerable own properties from one or more source objects to a target object. When the assign function is called, the following steps are taken:
- Let to be ToObject(target).
- ReturnIfAbrupt(to).
- If only one argument was passed, return to.
- Let sources be the List of argument values starting with the second argument.
- For each element nextSource of sources, in ascending index order, do
- If nextSource is undefined or null, let keys be an empty List.
- Else, …
…followed by the description of copying own properties. The draft of Object Rest/Spread Properties is here. It is not a part of the Ecma-262 6.0.
A SpreadElement in an array literal expression is defined to begin as follows:
SpreadElement : … AssignmentExpression
- Let spreadRef be the result of evaluating AssignmentExpression.
- Let spreadObj be GetValue(spreadRef).
- Let iterator be GetIterator(spreadObj).
- ReturnIfAbrupt(iterator).
And since undefined
does not have a property with the key @@iterator, a TypeError is thrown, based on the steps of GetIterator. The standard is not an easy read, but if I’m not mistaken, the path to error is GetIterator -> GetMethod -> GetV -> ToObject, which throws a TypeError for undefined and null.
A simple remedy to using variables with possibly undefined value in array initialization is to use a default:
const maybeArray = undefined;
const newArray = [ ...(maybeArray || []) ];
1: There is a difference in how setters are handled.