utilitish 0.0.6 → 0.0.7
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/dist/array/array-constructor.js +5 -5
- package/dist/array/array-constructor.spec.d.ts +2 -0
- package/dist/array/array-constructor.spec.js +130 -0
- package/dist/array/array-prototype.d.ts +341 -84
- package/dist/array/array-prototype.js +38 -53
- package/dist/array/array-prototype.spec.d.ts +1 -0
- package/dist/array/array-prototype.spec.js +536 -0
- package/dist/map/map-prototype.d.ts +69 -9
- package/dist/map/map-prototype.js +16 -3
- package/dist/map/map-prototype.spec.d.ts +1 -0
- package/dist/map/map-prototype.spec.js +261 -0
- package/dist/object/object-prototype.d.ts +61 -8
- package/dist/object/object-prototype.js +9 -0
- package/dist/object/object-prototype.spec.d.ts +1 -0
- package/dist/object/object-prototype.spec.js +110 -0
- package/dist/set/set-prototype.d.ts +83 -5
- package/dist/set/set-prototype.js +21 -6
- package/dist/set/set-prototype.spec.d.ts +1 -0
- package/dist/set/set-prototype.spec.js +122 -0
- package/dist/string/string-prototype.d.ts +143 -24
- package/dist/string/string-prototype.js +41 -11
- package/dist/string/string-prototype.spec.d.ts +1 -0
- package/dist/string/string-prototype.spec.js +115 -0
- package/dist/utils/logic.utils.d.ts +3 -0
- package/dist/utils/logic.utils.js +41 -0
- package/package.json +1 -1
- /package/dist/{utils.d.ts → utils/core.utils.d.ts} +0 -0
- /package/dist/{utils.js → utils/core.utils.js} +0 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
require("../map/map-prototype");
|
|
4
|
+
describe('Map.prototype', () => {
|
|
5
|
+
describe('toList()', () => {
|
|
6
|
+
describe('type: entries', () => {
|
|
7
|
+
it('should return array of entries by default', () => {
|
|
8
|
+
const map = new Map([
|
|
9
|
+
['a', 1],
|
|
10
|
+
['b', 2],
|
|
11
|
+
]);
|
|
12
|
+
expect(map.toList()).toEqual([
|
|
13
|
+
['a', 1],
|
|
14
|
+
['b', 2],
|
|
15
|
+
]);
|
|
16
|
+
});
|
|
17
|
+
it('should return entries when type is "entries"', () => {
|
|
18
|
+
const map = new Map([['x', 42]]);
|
|
19
|
+
expect(map.toList('entries')).toEqual([['x', 42]]);
|
|
20
|
+
});
|
|
21
|
+
it('should return empty array when map is empty', () => {
|
|
22
|
+
const map = new Map();
|
|
23
|
+
expect(map.toList()).toEqual([]);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('type: keys', () => {
|
|
27
|
+
it('should return array of keys when type is "keys"', () => {
|
|
28
|
+
const map = new Map([
|
|
29
|
+
['a', 1],
|
|
30
|
+
['b', 2],
|
|
31
|
+
]);
|
|
32
|
+
expect(map.toList('keys')).toEqual(['a', 'b']);
|
|
33
|
+
});
|
|
34
|
+
it('should return empty array of keys when map is empty', () => {
|
|
35
|
+
const map = new Map();
|
|
36
|
+
expect(map.toList('keys')).toEqual([]);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('type: values', () => {
|
|
40
|
+
it('should return array of values when type is "values"', () => {
|
|
41
|
+
const map = new Map([
|
|
42
|
+
['a', 1],
|
|
43
|
+
['b', 2],
|
|
44
|
+
]);
|
|
45
|
+
expect(map.toList('values')).toEqual([1, 2]);
|
|
46
|
+
});
|
|
47
|
+
it('should return empty array of values when map is empty', () => {
|
|
48
|
+
const map = new Map();
|
|
49
|
+
expect(map.toList('values')).toEqual([]);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('type: object', () => {
|
|
53
|
+
it('should return object when type is "object" with string keys', () => {
|
|
54
|
+
const map = new Map([
|
|
55
|
+
['a', 1],
|
|
56
|
+
['b', 2],
|
|
57
|
+
]);
|
|
58
|
+
expect(map.toList('object')).toEqual({ a: 1, b: 2 });
|
|
59
|
+
});
|
|
60
|
+
it('should return object with numeric keys converted to strings', () => {
|
|
61
|
+
const map = new Map([
|
|
62
|
+
[1, 'x'],
|
|
63
|
+
[2, 'y'],
|
|
64
|
+
]);
|
|
65
|
+
expect(map.toList('object')).toEqual({ '1': 'x', '2': 'y' });
|
|
66
|
+
});
|
|
67
|
+
it('should return empty object when map is empty', () => {
|
|
68
|
+
const map = new Map();
|
|
69
|
+
expect(map.toList('object')).toEqual({});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('error handling', () => {
|
|
73
|
+
it('should throw TypeError when key is not string/number/symbol for object conversion', () => {
|
|
74
|
+
const map = new Map();
|
|
75
|
+
const key = { foo: 1 };
|
|
76
|
+
map.set(key, 42);
|
|
77
|
+
expect(() => map.toList('object')).toThrow(TypeError);
|
|
78
|
+
});
|
|
79
|
+
it('should throw TypeError on unknown type', () => {
|
|
80
|
+
const map = new Map();
|
|
81
|
+
// @ts-expect-error
|
|
82
|
+
expect(() => map.toList('unknown')).toThrow(TypeError);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe('ensureArray()', () => {
|
|
87
|
+
describe('with non-existing keys', () => {
|
|
88
|
+
it('should return empty array and set it in map when key does not exist', () => {
|
|
89
|
+
const map = new Map();
|
|
90
|
+
const arr = map.ensureArray('foo');
|
|
91
|
+
expect(Array.isArray(arr)).toBe(true);
|
|
92
|
+
expect(arr).toEqual([]);
|
|
93
|
+
expect(map.get('foo')).toBe(arr);
|
|
94
|
+
});
|
|
95
|
+
it('should work with non-string keys', () => {
|
|
96
|
+
const key = { id: 1 };
|
|
97
|
+
const map = new Map();
|
|
98
|
+
const arr = map.ensureArray(key);
|
|
99
|
+
expect(Array.isArray(arr)).toBe(true);
|
|
100
|
+
expect(arr).toEqual([]);
|
|
101
|
+
expect(map.get(key)).toBe(arr);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe('with existing array values', () => {
|
|
105
|
+
it('should return existing array for existing key', () => {
|
|
106
|
+
const map = new Map();
|
|
107
|
+
map.set('bar', [1, 2]);
|
|
108
|
+
const arr = map.ensureArray('bar');
|
|
109
|
+
expect(arr).toEqual([1, 2]);
|
|
110
|
+
expect(map.get('bar')).toBe(arr);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('error handling', () => {
|
|
114
|
+
it('should throw TypeError when key is null or undefined', () => {
|
|
115
|
+
const map = new Map();
|
|
116
|
+
expect(() => map.ensureArray(null)).toThrow(TypeError);
|
|
117
|
+
expect(() => map.ensureArray(undefined)).toThrow(TypeError);
|
|
118
|
+
});
|
|
119
|
+
it('should throw TypeError when value for key is not an array', () => {
|
|
120
|
+
const map = new Map();
|
|
121
|
+
map.set('baz', 123);
|
|
122
|
+
expect(() => map.ensureArray('baz')).toThrow(TypeError);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
describe('toObject()', () => {
|
|
127
|
+
describe('with string keys', () => {
|
|
128
|
+
it('should convert Map with string keys to object', () => {
|
|
129
|
+
const map = new Map([
|
|
130
|
+
['a', 1],
|
|
131
|
+
['b', 2],
|
|
132
|
+
['c', 3],
|
|
133
|
+
]);
|
|
134
|
+
const obj = map.toObject();
|
|
135
|
+
expect(obj).toEqual({ a: 1, b: 2, c: 3 });
|
|
136
|
+
expect(typeof obj).toBe('object');
|
|
137
|
+
});
|
|
138
|
+
it('should preserve insertion order for string keys', () => {
|
|
139
|
+
const map = new Map([
|
|
140
|
+
['z', 26],
|
|
141
|
+
['a', 1],
|
|
142
|
+
['m', 13],
|
|
143
|
+
]);
|
|
144
|
+
const obj = map.toObject();
|
|
145
|
+
const keys = Object.keys(obj);
|
|
146
|
+
expect(keys).toEqual(['z', 'a', 'm']);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('with numeric keys', () => {
|
|
150
|
+
it('should convert Map with numeric keys to object', () => {
|
|
151
|
+
const map = new Map([
|
|
152
|
+
[1, 'a'],
|
|
153
|
+
[2, 'b'],
|
|
154
|
+
[3, 'c'],
|
|
155
|
+
]);
|
|
156
|
+
const obj = map.toObject();
|
|
157
|
+
expect(obj).toEqual({ 1: 'a', 2: 'b', 3: 'c' });
|
|
158
|
+
});
|
|
159
|
+
it('should convert Map with only numeric string keys', () => {
|
|
160
|
+
const map = new Map([
|
|
161
|
+
['1', 'one'],
|
|
162
|
+
['2', 'two'],
|
|
163
|
+
['3', 'three'],
|
|
164
|
+
]);
|
|
165
|
+
const obj = map.toObject();
|
|
166
|
+
expect(obj).toEqual({ 1: 'one', 2: 'two', 3: 'three' });
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
describe('with mixed key types', () => {
|
|
170
|
+
it('should convert Map with mixed string and numeric keys', () => {
|
|
171
|
+
const map = new Map([
|
|
172
|
+
['name', 'Alice'],
|
|
173
|
+
[1, 'one'],
|
|
174
|
+
['age', '30'],
|
|
175
|
+
[2, 'two'],
|
|
176
|
+
]);
|
|
177
|
+
const obj = map.toObject();
|
|
178
|
+
expect(obj).toEqual({
|
|
179
|
+
name: 'Alice',
|
|
180
|
+
1: 'one',
|
|
181
|
+
age: '30',
|
|
182
|
+
2: 'two',
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe('with symbol keys', () => {
|
|
187
|
+
it('should convert Map with symbol keys to object', () => {
|
|
188
|
+
const sym1 = Symbol('key1');
|
|
189
|
+
const sym2 = Symbol('key2');
|
|
190
|
+
const map = new Map([
|
|
191
|
+
[sym1, 'value1'],
|
|
192
|
+
[sym2, 'value2'],
|
|
193
|
+
]);
|
|
194
|
+
const obj = map.toObject();
|
|
195
|
+
expect(obj[sym1]).toBe('value1');
|
|
196
|
+
expect(obj[sym2]).toBe('value2');
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
describe('with various value types', () => {
|
|
200
|
+
it('should convert Map with various value types', () => {
|
|
201
|
+
const map = new Map([
|
|
202
|
+
['string', 'hello'],
|
|
203
|
+
['number', 42],
|
|
204
|
+
['boolean', true],
|
|
205
|
+
['null', null],
|
|
206
|
+
['array', [1, 2, 3]],
|
|
207
|
+
['object', { nested: 'value' }],
|
|
208
|
+
]);
|
|
209
|
+
const obj = map.toObject();
|
|
210
|
+
expect(obj).toEqual({
|
|
211
|
+
string: 'hello',
|
|
212
|
+
number: 42,
|
|
213
|
+
boolean: true,
|
|
214
|
+
null: null,
|
|
215
|
+
array: [1, 2, 3],
|
|
216
|
+
object: { nested: 'value' },
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
describe('with empty map', () => {
|
|
221
|
+
it('should convert empty Map to empty object', () => {
|
|
222
|
+
const map = new Map();
|
|
223
|
+
const obj = map.toObject();
|
|
224
|
+
expect(obj).toEqual({});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
describe('error handling', () => {
|
|
228
|
+
it('should throw TypeError when key is null', () => {
|
|
229
|
+
const map = new Map();
|
|
230
|
+
map.set('valid', 1);
|
|
231
|
+
map.set(null, 2);
|
|
232
|
+
expect(() => map.toObject()).toThrow(TypeError);
|
|
233
|
+
});
|
|
234
|
+
it('should throw TypeError when key is undefined', () => {
|
|
235
|
+
const map = new Map();
|
|
236
|
+
map.set('valid', 1);
|
|
237
|
+
map.set(undefined, 3);
|
|
238
|
+
expect(() => map.toObject()).toThrow(TypeError);
|
|
239
|
+
});
|
|
240
|
+
it('should throw TypeError when key is an object type', () => {
|
|
241
|
+
const map = new Map();
|
|
242
|
+
map.set({ invalid: 'key' }, 1);
|
|
243
|
+
expect(() => map.toObject()).toThrow(TypeError);
|
|
244
|
+
});
|
|
245
|
+
it('should throw TypeError when key is an array type', () => {
|
|
246
|
+
const map = new Map();
|
|
247
|
+
map.set(['invalid'], 'value');
|
|
248
|
+
expect(() => map.toObject()).toThrow(TypeError);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
describe('with duplicate keys', () => {
|
|
252
|
+
it('should overwrite earlier keys with the same name', () => {
|
|
253
|
+
const map = new Map();
|
|
254
|
+
map.set('key', 1);
|
|
255
|
+
map.set('key', 2);
|
|
256
|
+
const obj = map.toObject();
|
|
257
|
+
expect(obj).toEqual({ key: 2 });
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
});
|
|
@@ -2,20 +2,73 @@ export {};
|
|
|
2
2
|
declare global {
|
|
3
3
|
interface Object {
|
|
4
4
|
/**
|
|
5
|
-
* Creates a deep clone of the object.
|
|
6
|
-
*
|
|
5
|
+
* Creates a deep clone of the object using the structured clone algorithm.
|
|
6
|
+
* This preserves object types, reference integrity, and works with cyclic references.
|
|
7
|
+
*
|
|
8
|
+
* @template T The type of the object being cloned
|
|
9
|
+
* @this {T} The object to clone
|
|
10
|
+
* @returns {T} A deep copy of the original object with all nested objects and arrays cloned
|
|
11
|
+
* @throws {TypeError} If the object contains uncloneable values (functions, symbols, etc.)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const original = { a: { b: 1 }, c: [2, 3] };
|
|
15
|
+
* const cloned = original.deepClone();
|
|
16
|
+
* cloned.a.b = 999;
|
|
17
|
+
* console.log(original.a.b); // 1 (original unchanged)
|
|
18
|
+
*
|
|
19
|
+
* @remarks
|
|
20
|
+
* Uses the native `structuredClone()` API which provides:
|
|
21
|
+
* - Support for Date, Map, Set, TypedArray objects
|
|
22
|
+
* - Preservation of prototype chains for built-in types
|
|
23
|
+
* - Proper handling of circular references
|
|
7
24
|
*/
|
|
8
25
|
deepClone<T>(): T;
|
|
9
26
|
/**
|
|
10
|
-
* Deeply merges another object into the current object.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
27
|
+
* Deeply merges another object into the current object, recursively combining nested objects.
|
|
28
|
+
* Arrays and primitives are replaced (not merged). Modifies the current object in place.
|
|
29
|
+
*
|
|
30
|
+
* @template T The type of the object being merged into
|
|
31
|
+
* @this {T} The target object to merge into (will be modified)
|
|
32
|
+
* @param {object} source - The source object to merge from (must be a non-null object)
|
|
33
|
+
* @returns {T} The merged object (same as this)
|
|
34
|
+
* @throws {TypeError} If source is not a non-null object
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* const target = { a: 1, b: { c: 2 } };
|
|
38
|
+
* const source = { b: { d: 3 }, e: 4 };
|
|
39
|
+
* target.deepMerge(source);
|
|
40
|
+
* // target: { a: 1, b: { c: 2, d: 3 }, e: 4 }
|
|
41
|
+
*
|
|
42
|
+
* @remarks
|
|
43
|
+
* - Primitive values in the source overwrite target values
|
|
44
|
+
* - Arrays in the source completely replace target arrays (not merged element-wise)
|
|
45
|
+
* - Only enumerable own properties are merged
|
|
46
|
+
* - The merge is performed recursively for nested objects
|
|
13
47
|
*/
|
|
14
48
|
deepMerge<T>(source: any): T | typeof source;
|
|
15
49
|
/**
|
|
16
|
-
* Checks for deep equality with another object.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
50
|
+
* Checks for deep equality with another object, comparing all nested properties recursively.
|
|
51
|
+
* Uses strict equality for primitives and deep comparison for objects.
|
|
52
|
+
*
|
|
53
|
+
* @template T The type of the object
|
|
54
|
+
* @this {T} The object to compare
|
|
55
|
+
* @param {unknown} other - The object to compare with
|
|
56
|
+
* @returns {boolean} True if both objects are deeply equal, false otherwise
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* const obj1 = { a: { b: 1 }, c: [2, 3] };
|
|
60
|
+
* const obj2 = { a: { b: 1 }, c: [2, 3] };
|
|
61
|
+
* console.log(obj1.deepEquals(obj2)); // true
|
|
62
|
+
*
|
|
63
|
+
* const obj3 = { a: { b: 2 }, c: [2, 3] };
|
|
64
|
+
* console.log(obj1.deepEquals(obj3)); // false
|
|
65
|
+
*
|
|
66
|
+
* @remarks
|
|
67
|
+
* - Primitives are compared with strict equality (===)
|
|
68
|
+
* - Objects are compared property by property recursively
|
|
69
|
+
* - Array length and element order are considered
|
|
70
|
+
* - only enumerable own properties are compared
|
|
71
|
+
* - null and undefined are handled correctly
|
|
19
72
|
*/
|
|
20
73
|
deepEquals(other: unknown): boolean;
|
|
21
74
|
}
|
|
@@ -11,9 +11,15 @@ const defineIfNotExists = (name, fn) => {
|
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
|
+
/**
|
|
15
|
+
* @see Object.prototype.deepClone
|
|
16
|
+
*/
|
|
14
17
|
defineIfNotExists('deepClone', function () {
|
|
15
18
|
return structuredClone(this);
|
|
16
19
|
});
|
|
20
|
+
/**
|
|
21
|
+
* @see Object.prototype.deepMerge
|
|
22
|
+
*/
|
|
17
23
|
defineIfNotExists('deepMerge', function (source) {
|
|
18
24
|
if (typeof source !== 'object' || source === null) {
|
|
19
25
|
throw new TypeError('Source must be a non-null object');
|
|
@@ -34,6 +40,9 @@ defineIfNotExists('deepMerge', function (source) {
|
|
|
34
40
|
};
|
|
35
41
|
return merge(this.deepClone(), source);
|
|
36
42
|
});
|
|
43
|
+
/**
|
|
44
|
+
* @see Object.prototype.deepEquals
|
|
45
|
+
*/
|
|
37
46
|
defineIfNotExists('deepEquals', function (other) {
|
|
38
47
|
function eq(a, b) {
|
|
39
48
|
// Functions: always false (even if code is the same)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './object-prototype';
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
require("./object-prototype");
|
|
4
|
+
describe('Object.prototype', () => {
|
|
5
|
+
describe('deepClone()', () => {
|
|
6
|
+
it('should deeply clone a plain object', () => {
|
|
7
|
+
const obj = { a: 1, b: { c: 2 } };
|
|
8
|
+
const clone = structuredClone(obj);
|
|
9
|
+
expect(clone).toEqual(obj);
|
|
10
|
+
expect(clone).not.toBe(obj);
|
|
11
|
+
expect(clone.b).not.toBe(obj.b);
|
|
12
|
+
});
|
|
13
|
+
it('should deeply clone an array', () => {
|
|
14
|
+
const arr = [{ a: 1 }, { b: 2 }];
|
|
15
|
+
const clone = arr.deepClone();
|
|
16
|
+
expect(clone).toEqual(arr);
|
|
17
|
+
expect(clone).not.toBe(arr);
|
|
18
|
+
expect(clone[0]).not.toBe(arr[0]);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe('deepMerge()', () => {
|
|
22
|
+
it('should deeply merge two objects', () => {
|
|
23
|
+
const obj = { a: 1, b: { c: 2 } };
|
|
24
|
+
const source = { b: { d: 3 }, e: 4 };
|
|
25
|
+
const merged = obj.deepMerge(source);
|
|
26
|
+
expect(merged).toEqual({ a: 1, b: { c: 2, d: 3 }, e: 4 });
|
|
27
|
+
console.log(merged);
|
|
28
|
+
});
|
|
29
|
+
it('should overwrite primitive values', () => {
|
|
30
|
+
const obj = { a: 1, b: 2 };
|
|
31
|
+
const merged = obj.deepMerge({ b: 3 });
|
|
32
|
+
expect(merged).toEqual({ a: 1, b: 3 });
|
|
33
|
+
console.log(merged);
|
|
34
|
+
});
|
|
35
|
+
describe('error handling', () => {
|
|
36
|
+
it('should throw if source is not an object', () => {
|
|
37
|
+
expect(() => ({}).deepMerge(null)).toThrowError(TypeError);
|
|
38
|
+
expect(() => ({}).deepMerge(42)).toThrowError(TypeError);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe('deepEquals()', () => {
|
|
43
|
+
describe('with objects', () => {
|
|
44
|
+
it('should return true for objects with same keys and values, regardless of key order', () => {
|
|
45
|
+
expect({ a: 2, b: 3 }.deepEquals({ b: 3, a: 2 })).toBe(true);
|
|
46
|
+
expect({ a: 2, b: { c: 4 } }.deepEquals({ b: { c: 4 }, a: 2 })).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
it('should return false for objects with different values', () => {
|
|
49
|
+
expect({ a: 2, b: 3 }.deepEquals({ a: 2, b: 4 })).toBe(false);
|
|
50
|
+
expect({ a: 2, b: { c: 4 } }.deepEquals({ a: 2, b: { c: 5 } })).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
it('should return false for objects with different keys', () => {
|
|
53
|
+
expect({ a: 2 }.deepEquals({ b: 2 })).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
it('should return true for empty objects', () => {
|
|
56
|
+
expect({}.deepEquals({})).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
it('should return false for objects with different key counts', () => {
|
|
59
|
+
expect({ a: 1 }.deepEquals({ a: 1, b: 2 })).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('with arrays', () => {
|
|
63
|
+
it('should return true for arrays with same elements in same order', () => {
|
|
64
|
+
expect([1, 2, 3].deepEquals([1, 2, 3])).toBe(true);
|
|
65
|
+
expect([1, [2, 3]].deepEquals([1, [2, 3]])).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it('should return false for arrays with different elements or order', () => {
|
|
68
|
+
expect([1, 2, 3].deepEquals([1, 3, 2])).toBe(false);
|
|
69
|
+
expect([1, [2, 3]].deepEquals([1, [3, 2]])).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
it('should return true for empty arrays', () => {
|
|
72
|
+
expect([].deepEquals([])).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
it('should return false for arrays with different lengths', () => {
|
|
75
|
+
expect([1].deepEquals([1, 2])).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe('with special values', () => {
|
|
79
|
+
it('should return true for NaN deepEquals NaN', () => {
|
|
80
|
+
expect({ a: NaN }.deepEquals({ a: NaN })).toBe(true);
|
|
81
|
+
expect([NaN].deepEquals([NaN])).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
it('should return false for +0 and -0', () => {
|
|
84
|
+
expect({ a: +0 }.deepEquals({ a: -0 })).toBe(false);
|
|
85
|
+
expect([+0].deepEquals([-0])).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
it('should return false for objects with undefined vs missing keys', () => {
|
|
88
|
+
expect({ a: undefined }.deepEquals({})).toBe(false);
|
|
89
|
+
expect({}.deepEquals({ a: undefined })).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
it('should return false for objects with functions, even if functions are equal', () => {
|
|
92
|
+
expect({ a: () => 1 }.deepEquals({ a: () => 1 })).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe('with nested structures', () => {
|
|
96
|
+
it('should return true for nested structures deeply equal', () => {
|
|
97
|
+
expect({ a: [1, { b: 2 }] }.deepEquals({ a: [1, { b: 2 }] })).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
it('should return false for nested structures not deeply equal', () => {
|
|
100
|
+
expect({ a: [1, { b: 2 }] }.deepEquals({ a: [1, { b: 3 }] })).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe('type mismatches', () => {
|
|
104
|
+
it('should return false for array vs object', () => {
|
|
105
|
+
expect([].deepEquals({})).toBe(false);
|
|
106
|
+
expect({}.deepEquals([])).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -3,24 +3,102 @@ declare global {
|
|
|
3
3
|
interface Set<T> {
|
|
4
4
|
/**
|
|
5
5
|
* Converts the Set into an array, preserving insertion order.
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* @template T The type of elements in the Set
|
|
8
|
+
* @this {Set<T>} The Set to convert
|
|
9
|
+
* @returns {T[]} An array containing all values in the Set in insertion order
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const set = new Set([1, 2, 3]);
|
|
13
|
+
* const arr = set.toList(); // [1, 2, 3]
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* - Returns a new array instance each time; modifying it does not affect the Set
|
|
17
|
+
* - Empty sets return an empty array
|
|
7
18
|
*/
|
|
8
19
|
toList<T>(): T[];
|
|
9
20
|
/**
|
|
10
|
-
* Returns true if at least one of the given items is present in the
|
|
21
|
+
* Returns true if at least one of the given items is present in the Set.
|
|
22
|
+
*
|
|
23
|
+
* @template T The type of elements in the Set
|
|
24
|
+
* @this {Set<T>} The Set to check
|
|
25
|
+
* @param {...T[]} items - Variable number of items to check
|
|
26
|
+
* @returns {boolean} True if any item is in the Set, false if no items or Set is empty
|
|
27
|
+
* @throws {TypeError} If arguments are not in array-like form
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const set = new Set(['a', 'b', 'c']);
|
|
31
|
+
* set.hasAny('d', 'a'); // true
|
|
32
|
+
* set.hasAny('d', 'e'); // false
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* - Returns false if no items are provided (empty arguments)
|
|
36
|
+
* - Uses strict equality (===) to check for item presence
|
|
11
37
|
*/
|
|
12
38
|
hasAny(...items: T[]): boolean;
|
|
13
39
|
/**
|
|
14
|
-
* Returns true if all of the given items are present in the
|
|
40
|
+
* Returns true if all of the given items are present in the Set.
|
|
41
|
+
* Can accept items as separate arguments or as a single Set parameter.
|
|
42
|
+
*
|
|
43
|
+
* @template T The type of elements in the Set
|
|
44
|
+
* @this {Set<T>} The Set to check
|
|
45
|
+
* @param {...(T[] | Set<T>)} args - Items to check (varargs or single Set)
|
|
46
|
+
* @returns {boolean} True if all items are in the Set, false otherwise
|
|
47
|
+
* @throws {TypeError} If arguments are not in a valid format or not all Sets
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* const set = new Set(['a', 'b', 'c']);
|
|
51
|
+
* set.includes('a', 'b'); // true
|
|
52
|
+
* set.includes('a', 'd'); // false
|
|
53
|
+
* set.includes(new Set(['a', 'b'])); // true
|
|
54
|
+
*
|
|
55
|
+
* @remarks
|
|
56
|
+
* - Returns true if no arguments are provided (like Array's every())
|
|
57
|
+
* - Can check against another Set by passing it as the single argument
|
|
58
|
+
* - Uses strict equality (===) for item comparison
|
|
15
59
|
*/
|
|
16
60
|
includes(...items: T[]): boolean;
|
|
17
61
|
includes(items: Set<T>): boolean;
|
|
18
62
|
/**
|
|
19
|
-
* Returns a new Set that is the union of this
|
|
63
|
+
* Returns a new Set that is the union (combination) of this Set and all given Sets.
|
|
64
|
+
*
|
|
65
|
+
* @template T The type of elements in the Sets
|
|
66
|
+
* @this {Set<T>} The Set to start with
|
|
67
|
+
* @param {...Set<T>[]} others - Sets to union with
|
|
68
|
+
* @returns {Set<T>} A new Set containing all unique elements from this Set and all given Sets
|
|
69
|
+
* @throws {TypeError} If any argument is not a Set instance
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* const set1 = new Set([1, 2, 3]);
|
|
73
|
+
* const set2 = new Set([3, 4, 5]);
|
|
74
|
+
* const result = set1.union(set2); // Set(5) { 1, 2, 3, 4, 5 }
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* - Does not modify the original Set
|
|
78
|
+
* - Duplicate values across sets are automatically deduplicated (Set behavior)
|
|
79
|
+
* - Returns a new Set instance each time
|
|
20
80
|
*/
|
|
21
81
|
union(...others: Set<T>[]): Set<T>;
|
|
22
82
|
/**
|
|
23
|
-
* Returns a new Set that is the intersection of this
|
|
83
|
+
* Returns a new Set that is the intersection of this Set and all given Sets.
|
|
84
|
+
* Contains only elements that are present in all Sets (this + all given Sets).
|
|
85
|
+
*
|
|
86
|
+
* @template T The type of elements in the Sets
|
|
87
|
+
* @this {Set<T>} The Set to start with
|
|
88
|
+
* @param {...Set<T>[]} others - Sets to intersect with
|
|
89
|
+
* @returns {Set<T>} A new Set containing only elements that exist in all Sets
|
|
90
|
+
* @throws {TypeError} If any argument is not a Set instance
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* const set1 = new Set([1, 2, 3, 4]);
|
|
94
|
+
* const set2 = new Set([2, 3, 5]);
|
|
95
|
+
* const set3 = new Set([3, 6]);
|
|
96
|
+
* const result = set1.intersection(set2, set3); // Set(1) { 3 }
|
|
97
|
+
*
|
|
98
|
+
* @remarks
|
|
99
|
+
* - Does not modify the original Set
|
|
100
|
+
* - Returns an empty Set if no elements are common to all Sets
|
|
101
|
+
* - Returns a new Set instance each time
|
|
24
102
|
*/
|
|
25
103
|
intersection(...others: Set<T>[]): Set<T>;
|
|
26
104
|
}
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const core_utils_1 = require("../utils/core.utils");
|
|
4
|
+
/**
|
|
5
|
+
* @see Set.prototype.toList
|
|
6
|
+
*/
|
|
7
|
+
(0, core_utils_1.defineIfNotExists)(Set.prototype, 'toList', function () {
|
|
5
8
|
// Always returns a new array, even for empty sets
|
|
6
9
|
return Array.from(this);
|
|
7
10
|
});
|
|
8
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @see Set.prototype.hasAny
|
|
13
|
+
*/
|
|
14
|
+
(0, core_utils_1.defineIfNotExists)(Set.prototype, 'hasAny', function (...items) {
|
|
9
15
|
if (!Array.isArray(items))
|
|
10
16
|
throw new TypeError('Arguments must be an array');
|
|
11
17
|
if (items.length === 0)
|
|
12
18
|
return false;
|
|
13
19
|
return items.some((item) => this.has(item));
|
|
14
20
|
});
|
|
15
|
-
|
|
21
|
+
/**
|
|
22
|
+
* @see Set.prototype.includes
|
|
23
|
+
*/
|
|
24
|
+
(0, core_utils_1.defineIfNotExists)(Set.prototype, 'includes', function (...args) {
|
|
16
25
|
let values;
|
|
17
26
|
if (args.length === 0)
|
|
18
27
|
return true; // empty means "all included" (like [].every)
|
|
@@ -26,7 +35,10 @@ const utils_1 = require("../utils");
|
|
|
26
35
|
throw new TypeError('Arguments must be an array or a Set');
|
|
27
36
|
return values.every((item) => this.has(item));
|
|
28
37
|
});
|
|
29
|
-
|
|
38
|
+
/**
|
|
39
|
+
* @see Set.prototype.union
|
|
40
|
+
*/
|
|
41
|
+
(0, core_utils_1.defineIfNotExists)(Set.prototype, 'union', function (...others) {
|
|
30
42
|
const result = new Set(this);
|
|
31
43
|
for (const other of others) {
|
|
32
44
|
if (!(other instanceof Set))
|
|
@@ -37,7 +49,10 @@ const utils_1 = require("../utils");
|
|
|
37
49
|
}
|
|
38
50
|
return result;
|
|
39
51
|
});
|
|
40
|
-
|
|
52
|
+
/**
|
|
53
|
+
* @see Set.prototype.intersection
|
|
54
|
+
*/
|
|
55
|
+
(0, core_utils_1.defineIfNotExists)(Set.prototype, 'intersection', function (...others) {
|
|
41
56
|
if (others.some((s) => !(s instanceof Set)))
|
|
42
57
|
throw new TypeError('Arguments must be Sets');
|
|
43
58
|
const result = new Set();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '../set/set-prototype';
|