@radically-straightforward/utilities 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.3 · 2024-01-05
4
+
5
+ - Made `intern()` more strict in terms of types and provide auxiliary types for it.
6
+
3
7
  ## 0.0.2 · 2024-01-05
4
8
 
5
9
  - **Breaking Change:** Modified `intern()` to be shallow, which may be easier to reason about and faster.
package/README.md CHANGED
@@ -11,33 +11,61 @@ $ npm install @radically-straightforward/utilities
11
11
  ## Usage
12
12
 
13
13
  ```typescript
14
- import * as node from "@radically-straightforward/utilities";
14
+ import * as utilities from "@radically-straightforward/utilities";
15
15
  ```
16
16
 
17
17
  <!-- DOCUMENTATION START: ./source/index.mts -->
18
18
 
19
+ ### `Intern`
20
+
21
+ ```typescript
22
+ export type Intern<Type> = Readonly<
23
+ Type & {
24
+ [internSymbol]: true;
25
+ }
26
+ >;
27
+ ```
28
+
29
+ Utility type for `intern()`.
30
+
31
+ ### `InternInnerValue`
32
+
33
+ ```typescript
34
+ export type InternInnerValue =
35
+ | string
36
+ | number
37
+ | bigint
38
+ | boolean
39
+ | symbol
40
+ | undefined
41
+ | null
42
+ | Intern<unknown>;
43
+ ```
44
+
45
+ Utility type for `intern()`.
46
+
19
47
  ### `intern()`
20
48
 
21
49
  ```typescript
22
50
  export function intern<
23
51
  T extends
24
- | Array<unknown>
52
+ | Array<InternInnerValue>
25
53
  | {
26
- [key: string]: unknown;
54
+ [key: string]: InternInnerValue;
27
55
  },
28
- >(value: T): T;
56
+ >(value: T): Intern<T>;
29
57
  ```
30
58
 
31
59
  [Interning](<https://en.wikipedia.org/wiki/Interning_(computer_science)>) a value makes it unique across the program, which is useful for checking equality with `===` (reference equality), using it as a key in a `Map`, adding it to a `Set`, and so forth:
32
60
 
33
- ```javascript
61
+ ```typescript
34
62
  import { intern as $ } from "@radically-straightforward/utilities";
35
63
 
36
64
  [1] === [1]; // => false
37
65
  $([1]) === $([1]); // => true
38
66
 
39
67
  {
40
- const map = new Map();
68
+ const map = new Map<number[], number>();
41
69
  map.set([1], 1);
42
70
  map.set([1], 2);
43
71
  map.size; // => 2
@@ -45,7 +73,7 @@ $([1]) === $([1]); // => true
45
73
  }
46
74
 
47
75
  {
48
- const map = new Map();
76
+ const map = new Map<utilities.Intern<number[]>, number>();
49
77
  map.set($([1]), 1);
50
78
  map.set($([1]), 2);
51
79
  map.size; // => 1
@@ -53,7 +81,7 @@ $([1]) === $([1]); // => true
53
81
  }
54
82
 
55
83
  {
56
- const set = new Set();
84
+ const set = new Set<number[]>();
57
85
  set.add([1]);
58
86
  set.add([1]);
59
87
  set.size; // => 2
@@ -61,7 +89,7 @@ $([1]) === $([1]); // => true
61
89
  }
62
90
 
63
91
  {
64
- const set = new Set();
92
+ const set = new Set<utilities.Intern<number[]>>();
65
93
  set.add($([1]));
66
94
  set.add($([1]));
67
95
  set.size; // => 1
@@ -79,6 +107,8 @@ $([1]) === $([1]); // => true
79
107
 
80
108
  > **Note:** Interning a value is a costly operation which grows more expensive as you intern more values. Only intern values when really necessary.
81
109
 
110
+ > **Note:** Interned objects do not preserve the order of the attributes: `$({ a: 1, b: 2 }) === $({ b: 2, a: 1 })`.
111
+
82
112
  > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
83
113
 
84
114
  **Related Work**
package/build/index.d.mts CHANGED
@@ -1,14 +1,24 @@
1
+ /**
2
+ * Utility type for `intern()`.
3
+ */
4
+ export type Intern<Type> = Readonly<Type & {
5
+ [internSymbol]: true;
6
+ }>;
7
+ /**
8
+ * Utility type for `intern()`.
9
+ */
10
+ export type InternInnerValue = string | number | bigint | boolean | symbol | undefined | null | Intern<unknown>;
1
11
  /**
2
12
  * [Interning](<https://en.wikipedia.org/wiki/Interning_(computer_science)>) a value makes it unique across the program, which is useful for checking equality with `===` (reference equality), using it as a key in a `Map`, adding it to a `Set`, and so forth:
3
13
  *
4
- * ```javascript
14
+ * ```typescript
5
15
  * import { intern as $ } from "@radically-straightforward/utilities";
6
16
  *
7
17
  * [1] === [1]; // => false
8
18
  * $([1]) === $([1]); // => true
9
19
  *
10
20
  * {
11
- * const map = new Map();
21
+ * const map = new Map<number[], number>();
12
22
  * map.set([1], 1);
13
23
  * map.set([1], 2);
14
24
  * map.size; // => 2
@@ -16,7 +26,7 @@
16
26
  * }
17
27
  *
18
28
  * {
19
- * const map = new Map();
29
+ * const map = new Map<utilities.Intern<number[]>, number>();
20
30
  * map.set($([1]), 1);
21
31
  * map.set($([1]), 2);
22
32
  * map.size; // => 1
@@ -24,7 +34,7 @@
24
34
  * }
25
35
  *
26
36
  * {
27
- * const set = new Set();
37
+ * const set = new Set<number[]>();
28
38
  * set.add([1]);
29
39
  * set.add([1]);
30
40
  * set.size; // => 2
@@ -32,7 +42,7 @@
32
42
  * }
33
43
  *
34
44
  * {
35
- * const set = new Set();
45
+ * const set = new Set<utilities.Intern<number[]>>();
36
46
  * set.add($([1]));
37
47
  * set.add($([1]));
38
48
  * set.size; // => 1
@@ -50,6 +60,8 @@
50
60
  *
51
61
  * > **Note:** Interning a value is a costly operation which grows more expensive as you intern more values. Only intern values when really necessary.
52
62
  *
63
+ * > **Note:** Interned objects do not preserve the order of the attributes: `$({ a: 1, b: 2 }) === $({ b: 2, a: 1 })`.
64
+ *
53
65
  * > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
54
66
  *
55
67
  * **Related Work**
@@ -98,18 +110,24 @@
98
110
  * - <https://twitter.com/swannodette/status/1067962983924539392>
99
111
  * - <https://gist.github.com/modernserf/c000e62d40f678cf395e3f360b9b0e43>
100
112
  */
101
- export declare function intern<T extends Array<unknown> | {
102
- [key: string]: unknown;
103
- }>(value: T): T;
113
+ export declare function intern<T extends Array<InternInnerValue> | {
114
+ [key: string]: InternInnerValue;
115
+ }>(value: T): Intern<T>;
104
116
  export declare namespace intern {
105
- var interned: symbol;
106
- var pools: {
107
- tuple: Map<Symbol, WeakRef<any>>;
108
- record: Map<Symbol, WeakRef<any>>;
117
+ var pool: {
118
+ tuple: Map<Symbol, WeakRef<Readonly<InternInnerValue[] & {
119
+ [internSymbol]: true;
120
+ }>>>;
121
+ record: Map<Symbol, WeakRef<Readonly<{
122
+ [key: string]: InternInnerValue;
123
+ } & {
124
+ [internSymbol]: true;
125
+ }>>>;
109
126
  };
110
127
  var finalizationRegistry: FinalizationRegistry<{
111
128
  type: "tuple" | "record";
112
129
  key: Symbol;
113
130
  }>;
114
131
  }
132
+ export declare const internSymbol: unique symbol;
115
133
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,KAAK,CAAC,OAAO,CAAC,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,EAC1E,KAAK,EAAE,CAAC,GACP,CAAC,CA2CH;yBA7Ce,MAAM"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,GAAG;IAAE,CAAC,YAAY,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,SAAS,GACT,IAAI,GACJ,MAAM,CAAC,OAAO,CAAC,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqGG;AACH,wBAAgB,MAAM,CACpB,CAAC,SAAS,KAAK,CAAC,gBAAgB,CAAC,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAAA;CAAE,EACvE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CA2CrB;yBA7Ce,MAAM;;;;;;;;;;;;;;;;AA+CtB,eAAO,MAAM,YAAY,eAAmB,CAAC"}
package/build/index.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * [Interning](<https://en.wikipedia.org/wiki/Interning_(computer_science)>) a value makes it unique across the program, which is useful for checking equality with `===` (reference equality), using it as a key in a `Map`, adding it to a `Set`, and so forth:
3
3
  *
4
- * ```javascript
4
+ * ```typescript
5
5
  * import { intern as $ } from "@radically-straightforward/utilities";
6
6
  *
7
7
  * [1] === [1]; // => false
8
8
  * $([1]) === $([1]); // => true
9
9
  *
10
10
  * {
11
- * const map = new Map();
11
+ * const map = new Map<number[], number>();
12
12
  * map.set([1], 1);
13
13
  * map.set([1], 2);
14
14
  * map.size; // => 2
@@ -16,7 +16,7 @@
16
16
  * }
17
17
  *
18
18
  * {
19
- * const map = new Map();
19
+ * const map = new Map<utilities.Intern<number[]>, number>();
20
20
  * map.set($([1]), 1);
21
21
  * map.set($([1]), 2);
22
22
  * map.size; // => 1
@@ -24,7 +24,7 @@
24
24
  * }
25
25
  *
26
26
  * {
27
- * const set = new Set();
27
+ * const set = new Set<number[]>();
28
28
  * set.add([1]);
29
29
  * set.add([1]);
30
30
  * set.size; // => 2
@@ -32,7 +32,7 @@
32
32
  * }
33
33
  *
34
34
  * {
35
- * const set = new Set();
35
+ * const set = new Set<utilities.Intern<number[]>>();
36
36
  * set.add($([1]));
37
37
  * set.add($([1]));
38
38
  * set.size; // => 1
@@ -50,6 +50,8 @@
50
50
  *
51
51
  * > **Note:** Interning a value is a costly operation which grows more expensive as you intern more values. Only intern values when really necessary.
52
52
  *
53
+ * > **Note:** Interned objects do not preserve the order of the attributes: `$({ a: 1, b: 2 }) === $({ b: 2, a: 1 })`.
54
+ *
53
55
  * > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
54
56
  *
55
57
  * **Related Work**
@@ -107,7 +109,7 @@ export function intern(value) {
107
109
  throw new Error(`Failed to intern value.`);
108
110
  })();
109
111
  const keys = Object.keys(value);
110
- for (const internWeakRef of intern.pools[type].values()) {
112
+ for (const internWeakRef of intern.pool[type].values()) {
111
113
  const internValue = internWeakRef.deref();
112
114
  if (internValue === undefined ||
113
115
  keys.length !== Object.keys(internValue).length)
@@ -125,22 +127,22 @@ export function intern(value) {
125
127
  "undefined",
126
128
  ].includes(typeof innerValue) ||
127
129
  innerValue === null ||
128
- innerValue[intern.interned] === true))
130
+ innerValue[internSymbol] === true))
129
131
  throw new Error(`Failed to intern value because of non-interned inner value.`);
130
132
  const key = Symbol();
131
- value[intern.interned] = true;
133
+ value[internSymbol] = true;
132
134
  Object.freeze(value);
133
- intern.pools[type].set(key, new WeakRef(value));
135
+ intern.pool[type].set(key, new WeakRef(value));
134
136
  intern.finalizationRegistry.register(value, { type, key });
135
137
  return value;
136
138
  }
137
- intern.interned = Symbol("interned");
138
- intern.pools = {
139
+ export const internSymbol = Symbol("intern");
140
+ intern.pool = {
139
141
  tuple: new Map(),
140
142
  record: new Map(),
141
143
  };
142
144
  intern.finalizationRegistry = new FinalizationRegistry(({ type, key }) => {
143
- intern.pools[type].delete(key);
145
+ intern.pool[type].delete(key);
144
146
  });
145
147
  /*
146
148
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"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"}
@@ -2,9 +2,10 @@ import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import { intern as $ } from "./index.mjs";
4
4
  test("intern()", () => {
5
- // @ts-ignore
5
+ // @ts-expect-error
6
6
  assert(([1] === [1]) === false);
7
7
  assert($([1]) === $([1]));
8
+ assert($({ a: 1, b: 2 }) === $({ b: 2, a: 1 }));
8
9
  assert($([1]) !== $([2]));
9
10
  {
10
11
  const map = new Map();
@@ -35,10 +36,12 @@ test("intern()", () => {
35
36
  assert(set.has($([1])));
36
37
  }
37
38
  assert.throws(() => {
39
+ // @ts-expect-error
38
40
  $([1, {}]);
39
41
  });
40
42
  assert($([1, $({})]) === $([1, $({})]));
41
43
  assert.throws(() => {
44
+ // @ts-expect-error
42
45
  $([1])[0] = 2;
43
46
  });
44
47
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.mjs","sourceRoot":"","sources":["../source/index.test.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.test.mjs","sourceRoot":"","sources":["../source/index.test.mts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radically-straightforward/utilities",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "🛠️ Utilities for Node.js and the browser",
5
5
  "keywords": [
6
6
  "node",
package/source/index.mts CHANGED
@@ -1,14 +1,32 @@
1
+ /**
2
+ * Utility type for `intern()`.
3
+ */
4
+ export type Intern<Type> = Readonly<Type & { [internSymbol]: true }>;
5
+
6
+ /**
7
+ * Utility type for `intern()`.
8
+ */
9
+ export type InternInnerValue =
10
+ | string
11
+ | number
12
+ | bigint
13
+ | boolean
14
+ | symbol
15
+ | undefined
16
+ | null
17
+ | Intern<unknown>;
18
+
1
19
  /**
2
20
  * [Interning](<https://en.wikipedia.org/wiki/Interning_(computer_science)>) a value makes it unique across the program, which is useful for checking equality with `===` (reference equality), using it as a key in a `Map`, adding it to a `Set`, and so forth:
3
21
  *
4
- * ```javascript
22
+ * ```typescript
5
23
  * import { intern as $ } from "@radically-straightforward/utilities";
6
24
  *
7
25
  * [1] === [1]; // => false
8
26
  * $([1]) === $([1]); // => true
9
27
  *
10
28
  * {
11
- * const map = new Map();
29
+ * const map = new Map<number[], number>();
12
30
  * map.set([1], 1);
13
31
  * map.set([1], 2);
14
32
  * map.size; // => 2
@@ -16,7 +34,7 @@
16
34
  * }
17
35
  *
18
36
  * {
19
- * const map = new Map();
37
+ * const map = new Map<utilities.Intern<number[]>, number>();
20
38
  * map.set($([1]), 1);
21
39
  * map.set($([1]), 2);
22
40
  * map.size; // => 1
@@ -24,7 +42,7 @@
24
42
  * }
25
43
  *
26
44
  * {
27
- * const set = new Set();
45
+ * const set = new Set<number[]>();
28
46
  * set.add([1]);
29
47
  * set.add([1]);
30
48
  * set.size; // => 2
@@ -32,7 +50,7 @@
32
50
  * }
33
51
  *
34
52
  * {
35
- * const set = new Set();
53
+ * const set = new Set<utilities.Intern<number[]>>();
36
54
  * set.add($([1]));
37
55
  * set.add($([1]));
38
56
  * set.size; // => 1
@@ -50,6 +68,8 @@
50
68
  *
51
69
  * > **Note:** Interning a value is a costly operation which grows more expensive as you intern more values. Only intern values when really necessary.
52
70
  *
71
+ * > **Note:** Interned objects do not preserve the order of the attributes: `$({ a: 1, b: 2 }) === $({ b: 2, a: 1 })`.
72
+ *
53
73
  * > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
54
74
  *
55
75
  * **Related Work**
@@ -98,9 +118,9 @@
98
118
  * - <https://twitter.com/swannodette/status/1067962983924539392>
99
119
  * - <https://gist.github.com/modernserf/c000e62d40f678cf395e3f360b9b0e43>
100
120
  */
101
- export function intern<T extends Array<unknown> | { [key: string]: unknown }>(
102
- value: T,
103
- ): T {
121
+ export function intern<
122
+ T extends Array<InternInnerValue> | { [key: string]: InternInnerValue },
123
+ >(value: T): Intern<T> {
104
124
  const type = Array.isArray(value)
105
125
  ? "tuple"
106
126
  : typeof value === "object" && value !== null
@@ -109,15 +129,15 @@ export function intern<T extends Array<unknown> | { [key: string]: unknown }>(
109
129
  throw new Error(`Failed to intern value.`);
110
130
  })();
111
131
  const keys = Object.keys(value);
112
- for (const internWeakRef of intern.pools[type].values()) {
132
+ for (const internWeakRef of intern.pool[type].values()) {
113
133
  const internValue = internWeakRef.deref();
114
134
  if (
115
135
  internValue === undefined ||
116
136
  keys.length !== Object.keys(internValue).length
117
137
  )
118
138
  continue;
119
- if (keys.every((key) => (value as any)[key] === internValue[key]))
120
- return internValue;
139
+ if (keys.every((key) => (value as any)[key] === (internValue as any)[key]))
140
+ return internValue as any;
121
141
  }
122
142
  for (const innerValue of Object.values(value))
123
143
  if (
@@ -131,32 +151,35 @@ export function intern<T extends Array<unknown> | { [key: string]: unknown }>(
131
151
  "undefined",
132
152
  ].includes(typeof innerValue) ||
133
153
  innerValue === null ||
134
- (innerValue as any)[intern.interned] === true
154
+ (innerValue as any)[internSymbol] === true
135
155
  )
136
156
  )
137
157
  throw new Error(
138
158
  `Failed to intern value because of non-interned inner value.`,
139
159
  );
140
160
  const key = Symbol();
141
- (value as any)[intern.interned] = true;
161
+ (value as any)[internSymbol] = true;
142
162
  Object.freeze(value);
143
- intern.pools[type].set(key, new WeakRef(value));
163
+ intern.pool[type].set(key, new WeakRef(value as any));
144
164
  intern.finalizationRegistry.register(value, { type, key });
145
- return value;
165
+ return value as any;
146
166
  }
147
167
 
148
- intern.interned = Symbol("interned");
168
+ export const internSymbol = Symbol("intern");
149
169
 
150
- intern.pools = {
151
- tuple: new Map<Symbol, WeakRef<any>>(),
152
- record: new Map<Symbol, WeakRef<any>>(),
170
+ intern.pool = {
171
+ tuple: new Map<Symbol, WeakRef<Intern<InternInnerValue[]>>>(),
172
+ record: new Map<
173
+ Symbol,
174
+ WeakRef<Intern<{ [key: string]: InternInnerValue }>>
175
+ >(),
153
176
  };
154
177
 
155
178
  intern.finalizationRegistry = new FinalizationRegistry<{
156
179
  type: "tuple" | "record";
157
180
  key: Symbol;
158
181
  }>(({ type, key }) => {
159
- intern.pools[type].delete(key);
182
+ intern.pool[type].delete(key);
160
183
  });
161
184
 
162
185
  /*
@@ -4,13 +4,15 @@ import * as utilities from "./index.mjs";
4
4
  import { intern as $ } from "./index.mjs";
5
5
 
6
6
  test("intern()", () => {
7
- // @ts-ignore
7
+ // @ts-expect-error
8
8
  assert(([1] === [1]) === false);
9
9
  assert($([1]) === $([1]));
10
+ assert($({ a: 1, b: 2 }) === $({ b: 2, a: 1 }));
11
+
10
12
  assert($([1]) !== $([2]));
11
13
 
12
14
  {
13
- const map = new Map();
15
+ const map = new Map<number[], number>();
14
16
  map.set([1], 1);
15
17
  map.set([1], 2);
16
18
  assert.equal(map.size, 2);
@@ -18,7 +20,7 @@ test("intern()", () => {
18
20
  }
19
21
 
20
22
  {
21
- const map = new Map();
23
+ const map = new Map<utilities.Intern<number[]>, number>();
22
24
  map.set($([1]), 1);
23
25
  map.set($([1]), 2);
24
26
  assert.equal(map.size, 1);
@@ -26,7 +28,7 @@ test("intern()", () => {
26
28
  }
27
29
 
28
30
  {
29
- const set = new Set();
31
+ const set = new Set<number[]>();
30
32
  set.add([1]);
31
33
  set.add([1]);
32
34
  assert.equal(set.size, 2);
@@ -34,7 +36,7 @@ test("intern()", () => {
34
36
  }
35
37
 
36
38
  {
37
- const set = new Set();
39
+ const set = new Set<utilities.Intern<number[]>>();
38
40
  set.add($([1]));
39
41
  set.add($([1]));
40
42
  assert.equal(set.size, 1);
@@ -42,11 +44,13 @@ test("intern()", () => {
42
44
  }
43
45
 
44
46
  assert.throws(() => {
47
+ // @ts-expect-error
45
48
  $([1, {}]);
46
49
  });
47
50
  assert($([1, $({})]) === $([1, $({})]));
48
51
 
49
52
  assert.throws(() => {
53
+ // @ts-expect-error
50
54
  $([1])[0] = 2;
51
55
  });
52
56
  });