@radically-straightforward/utilities 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  });