|
10 | 10 |
|
11 | 11 | ## Motivation |
12 | 12 |
|
13 | | -Since its introduction, the [object spread syntax](https://github.com/tc39/proposal-object-rest-spread) has become a cornerstone of modern JavaScript. Its declarative nature makes it highly readable and maintainable. However, current syntax falls short when developers need to exclude keys from objects, especially in complex use cases with multiple spreads. |
| 13 | +Since its introduction, the [object spread syntax](https://github.com/tc39/proposal-object-rest-spread) has become a cornerstone of modern JavaScript. Its declarative nature makes it highly readable and maintainable. However, current syntax falls short when developers need to exclude keys from objects, especially in complex use cases with dynamic keys or intermediate exclusions. |
14 | 14 |
|
15 | 15 | --- |
16 | 16 |
|
17 | 17 | ## Proposal |
18 | 18 |
|
19 | 19 | ### Key Exclusion Syntax: `-key` |
20 | 20 |
|
21 | | -Introducing a concise, declarative way to exclude keys at any point in an object spread expression. The syntax dynamically removes the specified keys from all properties accumulated up to that point. |
| 21 | +The key exclusion syntax introduces a way to declaratively remove keys from objects during object spread operations. It works with: |
| 22 | +- **Static keys** |
| 23 | +- **Dynamic keys (e.g., values stored in variables)** |
| 24 | +- **Complex key expressions** |
22 | 25 |
|
23 | 26 | --- |
24 | 27 |
|
25 | 28 | ## Examples |
26 | 29 |
|
27 | | -### Basic Example: Key Exclusion at the End of Spread |
28 | | - |
29 | | -The exclusion syntax can be used to remove keys from the final merged result: |
| 30 | +### 1. Excluding Static Keys |
| 31 | +The most basic use case, excluding specific keys directly in the spread syntax: |
30 | 32 |
|
31 | 33 | ```js |
32 | | -const sanitizedOpts = (opts) => ({ |
33 | | - ...src, |
| 34 | +const sanitizedOpts = { |
34 | 35 | ...a, |
| 36 | + -key1, |
35 | 37 | ...b, |
36 | | - -keyToExclude, // Removes 'keyToExclude' from the final merged result |
37 | | -}); |
| 38 | + -key2, |
| 39 | +}; |
38 | 40 | ``` |
39 | 41 |
|
40 | | -### Using Exclusion in the Middle of a Spread |
41 | | - |
42 | | -The exclusion syntax can also be used in the middle of a spread expression, removing keys from the object as it is being built: |
| 42 | +### 2. Excluding Dynamic Keys (including Symbols) |
| 43 | +Dynamic keys, such as those stored in variables, can also be excluded: |
43 | 44 |
|
44 | 45 | ```js |
45 | | -const sanitizedOpts = (opts) => ({ |
46 | | - ...src, |
| 46 | +const sanitizedOpts = { |
47 | 47 | ...a, |
48 | | - -key1, // Removes 'key1' from { ...src, ...a } |
49 | | - ...b, // Spread 'b' after 'key1' is excluded |
50 | | -}); |
| 48 | + -[dynamicKey], // Excludes the value stored in `dynamicKey` |
| 49 | + ...b, |
| 50 | +}; |
51 | 51 | ``` |
52 | 52 |
|
53 | | -### Multiple Exclusions at Different Points |
| 53 | +### 3. Excluding Multiple Keys Dynamically |
| 54 | +You can exclude an array of keys using the spread operator within the exclusion syntax: |
54 | 55 |
|
55 | | -Keys can be excluded multiple times at different points in the spread expression: |
| 56 | +```js |
| 57 | +const sanitizedOpts = { |
| 58 | + ...a, |
| 59 | + -[...keysToExclude], // Excludes all keys in the `keysToExclude` array |
| 60 | + ...b, |
| 61 | +}; |
| 62 | +``` |
| 63 | + |
| 64 | +### 4. Using Complex Expressions as Keys |
| 65 | +Key expressions can be computed on-the-fly: |
56 | 66 |
|
57 | 67 | ```js |
58 | | -const sanitizedOpts = (opts) => ({ |
59 | | - ...src, |
60 | | - -key1, // Removes 'key1' from { ...src } |
| 68 | +const sanitizedOpts = { |
61 | 69 | ...a, |
62 | | - -key2, // Removes 'key2' from { ...src, ...a } |
| 70 | + -[dynamicPrefix + "Id"], // Removes a computed key like "userId" if `dynamicPrefix` is "user" |
63 | 71 | ...b, |
64 | | -}); |
| 72 | +}; |
65 | 73 | ``` |
66 | 74 |
|
67 | 75 | --- |
68 | 76 |
|
69 | 77 | ## Behavior |
70 | 78 |
|
71 | | -### Desugaring Examples |
| 79 | +### Execution Order |
| 80 | +Key exclusions operate sequentially during object spreading. The syntax removes the specified keys from all properties accumulated **up to that point**. |
72 | 81 |
|
73 | | -#### Exclude Key at the End |
74 | | -Input: |
75 | 82 | ```js |
76 | | -const sanitizedOpts = (opts) => ({ |
| 83 | +const sanitizedOpts = { |
77 | 84 | ...src, |
78 | 85 | ...a, |
| 86 | + -key1, // Removes 'key1' from { ...src, ...a } |
79 | 87 | ...b, |
80 | | - -keyToExclude, |
81 | | -}); |
82 | | -``` |
83 | | -Desugared: |
84 | | -```js |
85 | | -const sanitizedOpts = (opts) => { |
86 | | - const _$1 = { ...src, ...a, ...b }; |
87 | | - delete _$1.keyToExclude; |
88 | | - return _$1; |
89 | 88 | }; |
90 | 89 | ``` |
91 | 90 |
|
92 | | -#### Exclude Key in the Middle |
| 91 | +### Desugaring Examples |
| 92 | + |
| 93 | +#### 1. Excluding Static Keys |
93 | 94 | Input: |
94 | 95 | ```js |
95 | | -const sanitizedOpts = (opts) => ({ |
| 96 | +const sanitizedOpts = { |
96 | 97 | ...src, |
97 | | - ...a, |
98 | 98 | -key1, |
99 | | - ...b, |
100 | | -}); |
| 99 | + ...a, |
| 100 | +}; |
101 | 101 | ``` |
102 | 102 | Desugared: |
103 | 103 | ```js |
104 | | -const sanitizedOpts = (opts) => { |
105 | | - const _$1 = { ...src, ...a }; |
106 | | - delete _$1.key1; |
107 | | - const _$2 = { ..._$1, ...b }; |
108 | | - return _$2; |
109 | | -}; |
| 104 | +const sanitizedOpts = (() => { |
| 105 | + const _$1 = {}; |
| 106 | + for (const key in src) { |
| 107 | + if (key !== 'key1') _$1[key] = src[key]; |
| 108 | + } |
| 109 | + for (const key in a) { |
| 110 | + _$1[key] = a[key]; |
| 111 | + } |
| 112 | + return _$1; |
| 113 | +})(); |
110 | 114 | ``` |
111 | 115 |
|
112 | | -#### Multiple Exclusions at Different Points |
| 116 | +#### 2. Excluding Dynamic Keys |
113 | 117 | Input: |
114 | 118 | ```js |
115 | | -const sanitizedOpts = (opts) => ({ |
| 119 | +const sanitizedOpts = { |
116 | 120 | ...src, |
117 | | - -key1, |
| 121 | + -[dynamicKey], |
118 | 122 | ...a, |
119 | | - -key2, |
120 | | - ...b, |
121 | | -}); |
| 123 | +}; |
122 | 124 | ``` |
123 | 125 | Desugared: |
124 | 126 | ```js |
125 | | -const sanitizedOpts = (opts) => { |
126 | | - const _$1 = { ...src }; |
127 | | - delete _$1.key1; |
128 | | - const _$2 = { ..._$1, ...a }; |
129 | | - delete _$2.key2; |
130 | | - const _$3 = { ..._$2, ...b }; |
131 | | - return _$3; |
132 | | -}; |
| 127 | +const sanitizedOpts = (() => { |
| 128 | + const _$1 = {}; |
| 129 | + for (const key in src) { |
| 130 | + if (key !== dynamicKey) _$1[key] = src[key]; |
| 131 | + } |
| 132 | + for (const key in a) { |
| 133 | + _$1[key] = a[key]; |
| 134 | + } |
| 135 | + return _$1; |
| 136 | +})(); |
133 | 137 | ``` |
134 | 138 |
|
135 | | ---- |
136 | | - |
137 | | -### Performance-Optimized Desugaring Example |
138 | | - |
139 | | -For maximum performance, excluded keys can be omitted during the object-building process entirely, avoiding unnecessary copying or deletion. |
140 | | - |
141 | | -#### Exclude Key Without Any Copying |
| 139 | +#### 3. Excluding Multiple Dynamic Keys |
142 | 140 | Input: |
143 | 141 | ```js |
144 | | -const sanitizedOpts = (opts) => ({ |
| 142 | +const sanitizedOpts = { |
145 | 143 | ...src, |
| 144 | + -[...keysToExclude], |
146 | 145 | ...a, |
147 | | - -key1, |
148 | | - ...b, |
149 | | -}); |
| 146 | +}; |
150 | 147 | ``` |
151 | | -Optimized Desugared Code: |
| 148 | +Desugared: |
152 | 149 | ```js |
153 | | -const sanitizedOpts = (opts) => { |
| 150 | +const sanitizedOpts = (() => { |
154 | 151 | const _$1 = {}; |
155 | 152 | for (const key in src) { |
156 | | - if (key !== 'key1') _$1[key] = src[key]; |
| 153 | + if (!keysToExclude.includes(key)) _$1[key] = src[key]; |
157 | 154 | } |
158 | 155 | for (const key in a) { |
159 | | - if (key !== 'key1') _$1[key] = a[key]; |
160 | | - } |
161 | | - for (const key in b) { |
162 | | - _$1[key] = b[key]; // No exclusions here since key1 has already been removed |
| 156 | + _$1[key] = a[key]; |
163 | 157 | } |
164 | 158 | return _$1; |
165 | | -}; |
| 159 | +})(); |
166 | 160 | ``` |
167 | 161 |
|
168 | | -#### Exclude Multiple Keys Without Any Copying |
| 162 | +#### 4. Using Complex Expressions |
169 | 163 | Input: |
170 | 164 | ```js |
171 | | -const sanitizedOpts = (opts) => ({ |
| 165 | +const sanitizedOpts = { |
172 | 166 | ...src, |
| 167 | + -[dynamicPrefix + "Id"], |
173 | 168 | ...a, |
174 | | - -[...keysToExclude], |
175 | | - ...b, |
176 | | -}); |
| 169 | +}; |
177 | 170 | ``` |
178 | | -Optimized Desugared Code: |
| 171 | +Desugared: |
179 | 172 | ```js |
180 | | -const sanitizedOpts = (opts) => { |
| 173 | +const sanitizedOpts = (() => { |
181 | 174 | const _$1 = {}; |
| 175 | + const computedKey = dynamicPrefix + "Id"; |
182 | 176 | for (const key in src) { |
183 | | - if (!keysToExclude.includes(key)) _$1[key] = src[key]; |
| 177 | + if (key !== computedKey) _$1[key] = src[key]; |
184 | 178 | } |
185 | 179 | for (const key in a) { |
186 | | - if (!keysToExclude.includes(key)) _$1[key] = a[key]; |
187 | | - } |
188 | | - for (const key in b) { |
189 | | - _$1[key] = b[key]; // No exclusions here since all keys in keysToExclude are skipped before this point |
| 180 | + _$1[key] = a[key]; |
190 | 181 | } |
191 | 182 | return _$1; |
192 | | -}; |
| 183 | +})(); |
193 | 184 | ``` |
194 | 185 |
|
195 | 186 | --- |
196 | 187 |
|
197 | 188 | ## Advantages |
198 | 189 |
|
| 190 | +- **Supports Dynamic and Complex Keys**: Handles variables and expressions as keys. |
199 | 191 | - **Improved Readability**: Keeps code clean and declarative. |
200 | | -- **Fine-Grained Control**: Allows exclusions at specific points in the spread expression. |
201 | 192 | - **Performance Gains**: Avoids unnecessary property deletions by not copying excluded keys. |
202 | 193 | - **Reduced Boilerplate**: Simplifies exclusion patterns in complex merges. |
203 | 194 |
|
204 | 195 | --- |
205 | 196 |
|
206 | 197 | ## Conclusion |
207 | 198 |
|
208 | | -The proposed key exclusion syntax improves the flexibility and clarity of object spread operations, addressing common pain points in JavaScript development. Its ability to operate contextually within spread expressions and its potential for performance optimization make it a valuable addition to the language. |
| 199 | +The proposed key exclusion syntax enhances the flexibility and clarity of object spread operations. Its ability to operate contextually and handle complex, dynamic keys makes it a powerful tool for JavaScript developers. By eliminating unnecessary object copies or deletions, it also provides performance benefits in large-scale applications. |
209 | 200 |
|
210 | 201 | Contributions and feedback are welcome! |
0 commit comments