You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The assumption that it would be easier for the JS engines and transpilers to implement (because currently, the dash character is not expected before the key names in the object spread and thus won't conflict with any existing valid syntax).
134
+
The assumption is that it would be easier for the JS engines and transpilers to implement (because currently, the dash character is not expected before the key names in the object spread and thus won't conflict with any existing valid syntax).
135
135
136
136
137
-
### Execution order
137
+
####Execution order
138
138
139
-
Even though the minus operator is used in the object spread body, between the curly brackets, it must be applied to the result or spread operator (meaning that the result would be order-independent; please see [this comment](https://github.com/devlato/proposal-plus-minus-spread/issues/3#issuecomment-740308156) for details).
139
+
The key exclusion syntax would only be allowed at the end of the spread operator, before the closing curly bracket. Since the exclusion syntax removes the specified keys from the result, allowing it at other places inside the object spread (e.g., between multiple spread objects), is likely to cause an ambiguity.
140
140
141
-
Let's take an example:
142
-
143
-
```js
144
-
constresult= {
145
-
...objA,
146
-
...objB,
147
-
-keyToRemove,
148
-
};
149
-
```
150
-
151
-
It must be executed in the following order:
152
-
1. We merge objects `objA` and `objB` into a new object
153
-
2. We remove the key `keyToRemove` from that new object
154
-
3. We assign the result to a const `result`.
155
-
156
-
According to that, there's no difference at what position in the spread operator the minus operator is applied, so all these pieces of code would do the same:
157
-
158
-
```js
159
-
// Same
160
-
constresult= {
161
-
...objA,
162
-
...objB,
163
-
-keyToRemove,
164
-
};
165
-
166
-
// Also same!
167
-
constresult= {
168
-
...objA,
169
-
-keyToRemove,
170
-
...objB,
171
-
};
172
-
173
-
// And this!
174
-
constresult= {
175
-
-keyToRemove,
176
-
...objA,
177
-
...objB,
178
-
};
179
-
```
180
-
181
-
The second consequence is that in a particular object spread, a minus operator can be applied to each key only once.
182
-
183
-
184
-
### Alternatives
185
-
186
-
For sure, there are existing alternatives to the proposed minus operator.
187
-
188
-
1. The [delete operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete). Although, it cannot be used declaratively, meaning that the object produced by object spread must be assgned to a variable first, and only then we can remove the property from it by its key:
189
-
```js
190
-
constobj= {
191
-
a:'aValue',
192
-
b:'bValue',
193
-
key:'value',
194
-
};
195
-
196
-
// To avoid mutating the original object, we create a copy
197
-
constresult= { ...obj };
198
-
// Then we imperatively delete the key
199
-
deleteresult.key;
200
-
// And since the above line isn't an rvalue, we have to return the result explicitly
201
-
return result;
202
-
```
203
-
204
-
2.`Object.keys(obj).filter(...).reduce(...)` which is a block of repeated code that takes a bit of time to read and understand:
205
141
```js
206
-
constobj= {
207
-
a:'aValue',
208
-
b:'bValue',
209
-
key:'value',
210
-
};
211
-
212
-
// A bit wordy, ineffective, and too difficult to read
3. The third approach is even more imperative – it's `for ... of` loop or any related alternative.
220
-
```js
221
-
constobj= {
222
-
a:'aValue',
223
-
b:'bValue',
224
-
key:'value',
225
-
};
226
-
227
-
// A bit wordy `for ... of` loop
228
-
constresult= {};
229
-
for (const [key, value] ofObject.entries(obj)) {
230
-
if (key !=='key') {
231
-
result[key] = value;
232
-
}
233
-
}
234
-
// Also, loops aren't rvalues and thus, we have to return explicitly
235
-
return result;
236
-
```
237
-
238
-
4. Another possible solution is to manually assign all the properties, manually omitting the ones that has to be omitted. It's quite a lot boilerplate code.
239
-
```js
240
-
constobj= {
241
-
a:'aValue',
242
-
b:'bValue',
243
-
key:'value',
244
-
};
245
-
246
-
// `obj` might have too many properties here and this might be inconvenient
247
-
// to manually enumerate all of them
248
-
return {
249
-
a:obj.a,
250
-
b:obj.b,
251
-
};
252
-
```
253
-
254
-
5. Using spread with destructuring operator, to filter out all the undesired properties (for example, [`const { propertyIDontWant, ...newObject } = origObject; return newObject;`](https://github.com/devlato/proposal-plus-minus-spread/issues/1)).
255
-
```js
256
-
constobj= {
257
-
a:'aValue',
258
-
b:'bValue',
259
-
key:'value',
260
-
};
261
-
262
-
// `result` can't be returned directly from here, and also, an unnecessary variable `key` is created
263
-
const { key, ...result } = obj;
264
-
// So we have to return explicitly
265
-
return result;
266
-
```
267
-
268
-
6. And the last possible solution is to assign undefined to the key to be removed – but it's far from the ideal way to solve that issue, because the key would still exist in the object, meaning that if we enumerate through all the properties, there will be an undefined value for that key.
269
-
```js
270
-
constobj= {
271
-
a:'aValue',
272
-
b:'bValue',
273
-
key:'value',
274
-
};
275
-
276
-
return {
277
-
...obj,
278
-
// Even though `obj[key]` is `undefined`, `obj.hasOwnProperty('key')` would return true,
279
-
// producing undesired side effects
280
-
key:undefined,
142
+
// When the key name is known statically
143
+
constsanitizedOpts= (opts) => {
144
+
return {
145
+
...PRIVATE_OPTS,
146
+
-keyThatMustNotBeThere, // SyntaxError: the key exclusion syntax can only be used at the end of the object spread
147
+
..opts,
148
+
};
281
149
};
282
150
```
283
151
284
152
285
-
##Additional proposal: plus operator (optional)
153
+
### The key inclusion syntax
286
154
287
-
For the API consistency, I'm proposing to add a plus operator (expressed by a prepending `+` sign), which would allow to forcefully specify a value for a given key in the same manner. The main benefit of that is ability to explicitely override a particular key, without having to care about code lines order.
288
155
289
-
```js
290
-
constresult= {
291
-
...objA,
292
-
...objB,
293
-
+keyToAdd: valueToAdd,
294
-
};
295
-
```
156
+
## Specification
296
157
297
-
For instance:
158
+
WIP
298
159
299
-
```js
300
-
// Same
301
-
constresult= {
302
-
...objA,
303
-
...objB,
304
-
+keyToAdd:'test',
305
-
};
306
-
307
-
// Also same!
308
-
constresult= {
309
-
...objA,
310
-
+keyToAdd:'test',
311
-
...objB,
312
-
};
313
-
314
-
// And this!
315
-
constresult= {
316
-
+keyToAdd:'test',
317
-
...objA,
318
-
...objB,
319
-
};
320
-
```
321
160
322
-
All the variants of the code from the example above must be executed in the following order:
323
-
1. We merge objects `objA` and `objB` into a new object
324
-
2. We assign a value `'test'` to a key `keyToAdd` to that new object
325
-
3. We assign the result to a const `result`.
161
+
## Implementations
326
162
327
-
As a consequence of that, we can assert that in a particular object spread, a plus operator can be applied to each key only once.
0 commit comments