Skip to content

Commit 7f4872d

Browse files
authored
Update README.md
1 parent bac20c7 commit 7f4872d

File tree

1 file changed

+16
-180
lines changed

1 file changed

+16
-180
lines changed

README.md

Lines changed: 16 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -129,199 +129,35 @@ const sanitizedOpts = (opts) => {
129129
};
130130
```
131131

132-
#### Why the dash/the minus character?
132+
#### Why the dash/minus character?
133133

134-
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).
135135

136136

137-
### Execution order
137+
#### Execution order
138138

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.
140140

141-
Let's take an example:
142-
143-
```js
144-
const result = {
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-
const result = {
161-
...objA,
162-
...objB,
163-
-keyToRemove,
164-
};
165-
166-
// Also same!
167-
const result = {
168-
...objA,
169-
-keyToRemove,
170-
...objB,
171-
};
172-
173-
// And this!
174-
const result = {
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-
const obj = {
191-
a: 'aValue',
192-
b: 'bValue',
193-
key: 'value',
194-
};
195-
196-
// To avoid mutating the original object, we create a copy
197-
const result = { ...obj };
198-
// Then we imperatively delete the key
199-
delete result.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:
205141
```js
206-
const obj = {
207-
a: 'aValue',
208-
b: 'bValue',
209-
key: 'value',
210-
};
211-
212-
// A bit wordy, ineffective, and too difficult to read
213-
return Object
214-
.keys(obj)
215-
.filter((k) => k !== 'key')
216-
.reduce((acc, k) => ({ ...acc, [k]: obj[k] }), {});
217-
```
218-
219-
3. The third approach is even more imperative – it's `for ... of` loop or any related alternative.
220-
```js
221-
const obj = {
222-
a: 'aValue',
223-
b: 'bValue',
224-
key: 'value',
225-
};
226-
227-
// A bit wordy `for ... of` loop
228-
const result = {};
229-
for (const [key, value] of Object.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-
const obj = {
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-
const obj = {
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-
const obj = {
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+
const sanitizedOpts = (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+
};
281149
};
282150
```
283151

284152

285-
## Additional proposal: plus operator (optional)
153+
### The key inclusion syntax
286154

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.
288155

289-
```js
290-
const result = {
291-
...objA,
292-
...objB,
293-
+keyToAdd: valueToAdd,
294-
};
295-
```
156+
## Specification
296157

297-
For instance:
158+
WIP
298159

299-
```js
300-
// Same
301-
const result = {
302-
...objA,
303-
...objB,
304-
+keyToAdd: 'test',
305-
};
306-
307-
// Also same!
308-
const result = {
309-
...objA,
310-
+keyToAdd: 'test',
311-
...objB,
312-
};
313-
314-
// And this!
315-
const result = {
316-
+keyToAdd: 'test',
317-
...objA,
318-
...objB,
319-
};
320-
```
321160

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
326162

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.
163+
* Babel plugin – WIP

0 commit comments

Comments
 (0)