└── README.md
/README.md:
--------------------------------------------------------------------------------
1 | # Object.freeze and Object.seal syntax
2 |
3 | ## Rationale
4 |
5 | Object.freeze and Object.seal are both useful functions, but they aren't particularly ergonomic to use, especially when dealing with deeply nested objects or trying to do complex things like create a superset of a frozen object.
6 | Also even frozen objects can be modified via their prototype chain:
7 |
8 | ```js
9 | > const x = Object.freeze({});
10 | undefined
11 | > x.foo
12 | undefined
13 | > Object.prototype.foo = 3;
14 | 3
15 | > x.foo
16 | 3
17 | ```
18 |
19 | To prevent this you can use `Object.freeze({ __proto__: null })` or
20 | `Object.freeze(Object.assign(Object.create(null), {}))` to ensure that frozen
21 | objects cannot be modified via their prototype chain.
22 |
23 | ```js
24 | > const x = Object.freeze({ __proto__: null });
25 | undefined
26 | > x.foo
27 | undefined
28 | > Object.prototype.foo = 3;
29 | 3
30 | > x.foo
31 | undefined
32 | ```
33 |
34 | In addition, it would be useful to have these syntaxes in other places. It'd be useful to seal a destructuring expression, or freeze function arguments to create immutable bindings.
35 |
36 | ## Sketches
37 |
38 | ### Basic freezing of an object
39 |
40 | ```js
41 | const foo = {#
42 | a: {#
43 | b: {#
44 | c: {#
45 | d: {#
46 | e: [# "some string!" #]
47 | #}
48 | #}
49 | #}
50 | #}
51 | #}
52 | ```
53 |
54 | Click here to see the desugared version
55 |
56 | ```js
57 | const foo = Object.freeze({
58 | __proto__: null,
59 | a: Object.freeze({
60 | __proto__: null,
61 | b: Object.freeze({
62 | __proto__: null,
63 | c: Object.freeze({
64 | __proto__: null,
65 | d: Object.freeze({
66 | __proto__: null,
67 | e: Object.freeze([ "some string!" ])
68 | })
69 | })
70 | })
71 | })
72 | })
73 | ```
74 |
75 |
76 |
77 | ### Basic sealing of an object
78 |
79 | ```js
80 | const foo = {|
81 | a: {|
82 | b: {|
83 | c: {|
84 | d: {|
85 | e: [| "some string!" |]
86 | |}
87 | |}
88 | |}
89 | |}
90 | |}
91 | ```
92 |
93 | Click here to see the desugared version
94 |
95 | ```js
96 | const foo = Object.seal({
97 | __proto__: null,
98 | a: Object.seal({
99 | __proto__: null,
100 | b: Object.seal({
101 | __proto__: null,
102 | c: Object.seal({
103 | __proto__: null,
104 | d: Object.seal({
105 | __proto__: null,
106 | e: Object.seal(["some string!"])
107 | })
108 | })
109 | })
110 | })
111 | })
112 | ```
113 |
114 |
115 |
116 |
117 | ### Sealing a functions destructured options bag
118 |
119 | ```js
120 | function ajax({| url, headers, onSuccess |}) {
121 | fetch(url, { headers }).then(onSuccess)
122 | }
123 | ajax({ url: 'http://example.com', onsuccess: console.log })
124 | // throws TypeError('cannot define property `onsuccess`. Object is not extensible')
125 | ```
126 |
127 | Click here to see the desugared version
128 |
129 | ```js
130 | function ajax(_ref1) {
131 | const _ref2 = Object.seal({ url: undefined, headers: undefined, onSuccess: undefined })
132 | Object.assign(_ref2, _ref1)
133 | let url = _ref2.url
134 | let headers = _ref2.headers
135 | let onSuccess = _ref2.onSuccess
136 |
137 | fetch(url, { headers }).then(onSuccess)
138 | }
139 | ajax({ url: 'http://example.com', onsuccess: console.log })
140 | // throws TypeError('cannot define property `onsuccess`. Object is not extensible')
141 | ```
142 |
143 |
144 |
145 | ### Freezing a functions destructured options bag
146 |
147 | ```js
148 | function ajax({# url, headers, onSuccess #}) {
149 | url = new URL(url) // throws TypeError('cannot assign to const `url`')
150 | fetch(url, { headers }).then(onSuccess)
151 | }
152 | ajax({ url: 'http://example.com', onSuccess: console.log })
153 | ```
154 |
155 | Click here to see the desugared version
156 |
157 | ```js
158 | function ajax(_ref1) {
159 | const _ref2 = Object.seal({ url: undefined, headers: undefined, onSuccess: undefined }) // seal now, const later
160 | Object.assign(_ref2, _ref1)
161 | const url = _ref2.url
162 | const headers = _ref2.headers
163 | const onSuccess = _ref2.onSuccess
164 |
165 | url = new URL(url) // throws TypeError('cannot assign to const `url`')
166 | fetch(url, { headers }).then(onSuccess)
167 | }
168 | ajax({ url: 'http://example.com', onSuccess: console.log })
169 | ```
170 |
171 |
172 |
173 |
174 | ## Potential extensions - sketches
175 |
176 | ### Sealed function param bindings
177 |
178 | ```js
179 | function add(| a, b |) {
180 | return a + b
181 | }
182 | add(2, 2, 2) === 6
183 | // throws TypeError('invalid third parameter, expected 2`)
184 | ```
185 |
186 | Click here to see the desugared version
187 |
188 | ```js
189 | function add(_1, _2) {
190 | if (arguments.length > 2) {
191 | throws TypeError('invalid third parameter, expected 2')
192 | }
193 | let a = arguments[0]
194 | let b = arguments[1]
195 |
196 | return a + b
197 | }
198 | add(2, 2, 2) === 6
199 | // throws TypeError('invalid third parameter, expected 2`)
200 | ```
201 |
202 |
203 |
204 | ### Frozen function param bindings
205 |
206 | ```js
207 | function add1(# a #) {
208 | a += 1 // throws TypeError `invalid assignment...`
209 | return a
210 | }
211 | add1(1) === 2
212 | ```
213 |
214 | Click here to see the desugared version
215 |
216 | ```js
217 | function add1(_1) {
218 | if (arguments.length > 1) {
219 | throws TypeError('invalid second parameter, expected 1')
220 | }
221 | const a = arguments[0]
222 |
223 | a += 1 // throws TypeError `invalid assignment...`
224 | return a
225 | }
226 | add1(1) === 2
227 | ```
228 |
229 |
230 |
231 | ### Extending syntax to general destructing for better type safety
232 |
233 | ```js
234 | const foo = { a: 1, b: 2 }
235 | const {| a, b, c |} = foo
236 | // Throws TypeError 'invalid assignment to unknown property c'
237 | ```
238 |
239 | Click here to see the desugared version
240 |
241 | ```js
242 | const foo = { a: 1, b: 2 }
243 | if (!('a' in foo)) throw new TypeError('invalid assignment to unknown property a')
244 | const a = foo.a
245 | if (!('b' in foo)) throw new TypeError('invalid assignment to unknown property b')
246 | const b = foo.b
247 | if (!('c' in foo)) throw new TypeError('invalid assignment to unknown property c')
248 | const c = foo.c
249 | // Throws TypeError 'invalid assignment to unknown property c'
250 | ```
251 |
252 |
253 |
--------------------------------------------------------------------------------