ts-data-forge 1.0.0
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/LICENSE +201 -0
- package/README.md +534 -0
- package/package.json +101 -0
- package/src/array/array-utils-creation.test.mts +443 -0
- package/src/array/array-utils-modification.test.mts +197 -0
- package/src/array/array-utils-overload-type-error.test.mts +149 -0
- package/src/array/array-utils-reducing-value.test.mts +425 -0
- package/src/array/array-utils-search.test.mts +169 -0
- package/src/array/array-utils-set-op.test.mts +335 -0
- package/src/array/array-utils-slice-clamped.test.mts +113 -0
- package/src/array/array-utils-slicing.test.mts +316 -0
- package/src/array/array-utils-transformation.test.mts +790 -0
- package/src/array/array-utils-validation.test.mts +492 -0
- package/src/array/array-utils.mts +4000 -0
- package/src/array/array.test.mts +146 -0
- package/src/array/index.mts +2 -0
- package/src/array/tuple-utils.mts +519 -0
- package/src/array/tuple-utils.test.mts +518 -0
- package/src/collections/imap-mapped.mts +801 -0
- package/src/collections/imap-mapped.test.mts +860 -0
- package/src/collections/imap.mts +651 -0
- package/src/collections/imap.test.mts +932 -0
- package/src/collections/index.mts +6 -0
- package/src/collections/iset-mapped.mts +889 -0
- package/src/collections/iset-mapped.test.mts +1187 -0
- package/src/collections/iset.mts +682 -0
- package/src/collections/iset.test.mts +1084 -0
- package/src/collections/queue.mts +390 -0
- package/src/collections/queue.test.mts +282 -0
- package/src/collections/stack.mts +423 -0
- package/src/collections/stack.test.mts +225 -0
- package/src/expect-type.mts +206 -0
- package/src/functional/index.mts +4 -0
- package/src/functional/match.mts +300 -0
- package/src/functional/match.test.mts +177 -0
- package/src/functional/optional.mts +733 -0
- package/src/functional/optional.test.mts +619 -0
- package/src/functional/pipe.mts +212 -0
- package/src/functional/pipe.test.mts +85 -0
- package/src/functional/result.mts +1134 -0
- package/src/functional/result.test.mts +777 -0
- package/src/globals.d.mts +38 -0
- package/src/guard/has-key.mts +119 -0
- package/src/guard/has-key.test.mts +219 -0
- package/src/guard/index.mts +7 -0
- package/src/guard/is-non-empty-string.mts +108 -0
- package/src/guard/is-non-empty-string.test.mts +91 -0
- package/src/guard/is-non-null-object.mts +106 -0
- package/src/guard/is-non-null-object.test.mts +90 -0
- package/src/guard/is-primitive.mts +165 -0
- package/src/guard/is-primitive.test.mts +102 -0
- package/src/guard/is-record.mts +153 -0
- package/src/guard/is-record.test.mts +112 -0
- package/src/guard/is-type.mts +450 -0
- package/src/guard/is-type.test.mts +496 -0
- package/src/guard/key-is-in.mts +163 -0
- package/src/guard/key-is-in.test.mts +19 -0
- package/src/index.mts +10 -0
- package/src/iterator/index.mts +1 -0
- package/src/iterator/range.mts +120 -0
- package/src/iterator/range.test.mts +33 -0
- package/src/json/index.mts +1 -0
- package/src/json/json.mts +711 -0
- package/src/json/json.test.mts +628 -0
- package/src/number/branded-types/finite-number.mts +354 -0
- package/src/number/branded-types/finite-number.test.mts +135 -0
- package/src/number/branded-types/index.mts +26 -0
- package/src/number/branded-types/int.mts +278 -0
- package/src/number/branded-types/int.test.mts +140 -0
- package/src/number/branded-types/int16.mts +192 -0
- package/src/number/branded-types/int16.test.mts +170 -0
- package/src/number/branded-types/int32.mts +193 -0
- package/src/number/branded-types/int32.test.mts +170 -0
- package/src/number/branded-types/non-negative-finite-number.mts +223 -0
- package/src/number/branded-types/non-negative-finite-number.test.mts +188 -0
- package/src/number/branded-types/non-negative-int16.mts +187 -0
- package/src/number/branded-types/non-negative-int16.test.mts +201 -0
- package/src/number/branded-types/non-negative-int32.mts +187 -0
- package/src/number/branded-types/non-negative-int32.test.mts +204 -0
- package/src/number/branded-types/non-zero-finite-number.mts +229 -0
- package/src/number/branded-types/non-zero-finite-number.test.mts +198 -0
- package/src/number/branded-types/non-zero-int.mts +167 -0
- package/src/number/branded-types/non-zero-int.test.mts +177 -0
- package/src/number/branded-types/non-zero-int16.mts +196 -0
- package/src/number/branded-types/non-zero-int16.test.mts +195 -0
- package/src/number/branded-types/non-zero-int32.mts +196 -0
- package/src/number/branded-types/non-zero-int32.test.mts +197 -0
- package/src/number/branded-types/non-zero-safe-int.mts +196 -0
- package/src/number/branded-types/non-zero-safe-int.test.mts +232 -0
- package/src/number/branded-types/non-zero-uint16.mts +189 -0
- package/src/number/branded-types/non-zero-uint16.test.mts +199 -0
- package/src/number/branded-types/non-zero-uint32.mts +189 -0
- package/src/number/branded-types/non-zero-uint32.test.mts +199 -0
- package/src/number/branded-types/positive-finite-number.mts +241 -0
- package/src/number/branded-types/positive-finite-number.test.mts +204 -0
- package/src/number/branded-types/positive-int.mts +304 -0
- package/src/number/branded-types/positive-int.test.mts +176 -0
- package/src/number/branded-types/positive-int16.mts +188 -0
- package/src/number/branded-types/positive-int16.test.mts +197 -0
- package/src/number/branded-types/positive-int32.mts +188 -0
- package/src/number/branded-types/positive-int32.test.mts +197 -0
- package/src/number/branded-types/positive-safe-int.mts +187 -0
- package/src/number/branded-types/positive-safe-int.test.mts +210 -0
- package/src/number/branded-types/positive-uint16.mts +188 -0
- package/src/number/branded-types/positive-uint16.test.mts +203 -0
- package/src/number/branded-types/positive-uint32.mts +188 -0
- package/src/number/branded-types/positive-uint32.test.mts +203 -0
- package/src/number/branded-types/safe-int.mts +291 -0
- package/src/number/branded-types/safe-int.test.mts +170 -0
- package/src/number/branded-types/safe-uint.mts +187 -0
- package/src/number/branded-types/safe-uint.test.mts +176 -0
- package/src/number/branded-types/uint.mts +179 -0
- package/src/number/branded-types/uint.test.mts +158 -0
- package/src/number/branded-types/uint16.mts +186 -0
- package/src/number/branded-types/uint16.test.mts +170 -0
- package/src/number/branded-types/uint32.mts +218 -0
- package/src/number/branded-types/uint32.test.mts +170 -0
- package/src/number/enum/index.mts +2 -0
- package/src/number/enum/int8.mts +344 -0
- package/src/number/enum/int8.test.mts +180 -0
- package/src/number/enum/uint8.mts +293 -0
- package/src/number/enum/uint8.test.mts +164 -0
- package/src/number/index.mts +4 -0
- package/src/number/num.mts +604 -0
- package/src/number/num.test.mts +242 -0
- package/src/number/refined-number-utils.mts +566 -0
- package/src/object/index.mts +1 -0
- package/src/object/object.mts +447 -0
- package/src/object/object.test.mts +124 -0
- package/src/others/cast-mutable.mts +113 -0
- package/src/others/cast-readonly.mts +192 -0
- package/src/others/cast-readonly.test.mts +89 -0
- package/src/others/if-then.mts +98 -0
- package/src/others/if-then.test.mts +75 -0
- package/src/others/index.mts +7 -0
- package/src/others/map-nullable.mts +172 -0
- package/src/others/map-nullable.test.mts +297 -0
- package/src/others/memoize-function.mts +196 -0
- package/src/others/memoize-function.test.mts +168 -0
- package/src/others/tuple.mts +160 -0
- package/src/others/tuple.test.mts +11 -0
- package/src/others/unknown-to-string.mts +215 -0
- package/src/others/unknown-to-string.test.mts +114 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
import { expectType } from '../expect-type.mjs';
|
|
2
|
+
import { Tpl } from './tuple-utils.mjs';
|
|
3
|
+
|
|
4
|
+
describe('Tpl.map', () => {
|
|
5
|
+
const mapped = Tpl.map([1, 2, 3], (x, i): number => x * x * i);
|
|
6
|
+
|
|
7
|
+
expectType<typeof mapped, ArrayOfLength<3, number>>('=');
|
|
8
|
+
|
|
9
|
+
test('case 1', () => {
|
|
10
|
+
expect(mapped).toStrictEqual([0, 4, 18]);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('should work with empty tuple', () => {
|
|
14
|
+
const empty = [] as const;
|
|
15
|
+
const mappedEmpty = Tpl.map(empty, (x) => String(x));
|
|
16
|
+
expectType<typeof mappedEmpty, readonly []>('=');
|
|
17
|
+
expect(mappedEmpty).toStrictEqual([]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('should preserve tuple length with different types', () => {
|
|
21
|
+
const mixed = [1, 'hello', true] as const;
|
|
22
|
+
const mappedMixed = Tpl.map(mixed, (x) => typeof x);
|
|
23
|
+
expectType<typeof mappedMixed, readonly [string, string, string]>('<=');
|
|
24
|
+
expect(mappedMixed).toStrictEqual(['number', 'string', 'boolean']);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('should work with index parameter', () => {
|
|
28
|
+
const tuple = ['a', 'b', 'c'] as const;
|
|
29
|
+
const mappedWithIndex = Tpl.map(tuple, (x, i) => `${i}:${x}`);
|
|
30
|
+
expectType<typeof mappedWithIndex, readonly [string, string, string]>('<=');
|
|
31
|
+
expect(mappedWithIndex).toStrictEqual(['0:a', '1:b', '2:c']);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('Tpl.set', () => {
|
|
36
|
+
const result = Tpl.set([1, 2, 3], 1, 4);
|
|
37
|
+
|
|
38
|
+
expectType<typeof result, readonly [1 | 4, 2 | 4, 3 | 4]>('=');
|
|
39
|
+
|
|
40
|
+
test('case 1', () => {
|
|
41
|
+
expect(result).toStrictEqual([1, 4, 3]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('should work with different value types', () => {
|
|
45
|
+
const nums = [1, 2, 3] as const;
|
|
46
|
+
const withString = Tpl.set(nums, 1, 'two');
|
|
47
|
+
expectType<typeof withString, readonly [1 | 'two', 2 | 'two', 3 | 'two']>(
|
|
48
|
+
'=',
|
|
49
|
+
);
|
|
50
|
+
expect(withString).toStrictEqual([1, 'two', 3]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('should work at index 0', () => {
|
|
54
|
+
const tuple = ['a', 'b', 'c'] as const;
|
|
55
|
+
const updated = Tpl.set(tuple, 0, 'A');
|
|
56
|
+
expect(updated).toStrictEqual(['A', 'b', 'c']);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('should work at last index', () => {
|
|
60
|
+
const tuple = ['a', 'b', 'c'] as const;
|
|
61
|
+
const updated = Tpl.set(tuple, 2, 'C');
|
|
62
|
+
expect(updated).toStrictEqual(['a', 'b', 'C']);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('Tpl.toUpdated', () => {
|
|
67
|
+
const result = Tpl.toUpdated([1, 2, 3], 1, (x) => x + 2);
|
|
68
|
+
|
|
69
|
+
expectType<typeof result, readonly [number, number, number]>('=');
|
|
70
|
+
|
|
71
|
+
test('case 1', () => {
|
|
72
|
+
expect(result).toStrictEqual([1, 4, 3]);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('should work with complex transformations', () => {
|
|
76
|
+
const objects = [{ count: 1 }, { count: 2 }, { count: 3 }] as const;
|
|
77
|
+
const updated = Tpl.toUpdated(objects, 1, (obj) => ({
|
|
78
|
+
count: obj.count * 10,
|
|
79
|
+
}));
|
|
80
|
+
expect(updated).toStrictEqual([{ count: 1 }, { count: 20 }, { count: 3 }]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('should work with type changing updater', () => {
|
|
84
|
+
const nums = [1, 2, 3] as const;
|
|
85
|
+
const updated = Tpl.toUpdated(nums, 0, (n) => `Number: ${n}`);
|
|
86
|
+
expectType<typeof updated, readonly [string | 1, 2 | string, 3 | string]>(
|
|
87
|
+
'<=',
|
|
88
|
+
);
|
|
89
|
+
expect(updated).toStrictEqual(['Number: 1', 2, 3]);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('Tpl.toReversed', () => {
|
|
94
|
+
{
|
|
95
|
+
const result = Tpl.toReversed([1, 2, 3]);
|
|
96
|
+
|
|
97
|
+
expectType<typeof result, readonly [3, 2, 1]>('=');
|
|
98
|
+
|
|
99
|
+
test('case 1', () => {
|
|
100
|
+
expect(result).toStrictEqual([3, 2, 1]);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
test('should work with empty tuple', () => {
|
|
105
|
+
const empty = [] as const;
|
|
106
|
+
const reversed = Tpl.toReversed(empty);
|
|
107
|
+
expectType<typeof reversed, readonly []>('=');
|
|
108
|
+
expect(reversed).toStrictEqual([]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('should work with single element', () => {
|
|
112
|
+
const single = [42] as const;
|
|
113
|
+
const reversed = Tpl.toReversed(single);
|
|
114
|
+
expectType<typeof reversed, readonly [42]>('=');
|
|
115
|
+
expect(reversed).toStrictEqual([42]);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('should preserve mixed types in reverse order', () => {
|
|
119
|
+
const mixed = [1, 'hello', true, null] as const;
|
|
120
|
+
const reversed = Tpl.toReversed(mixed);
|
|
121
|
+
expectType<typeof reversed, readonly [null, true, 'hello', 1]>('=');
|
|
122
|
+
expect(reversed).toStrictEqual([null, true, 'hello', 1]);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('Tpl.toSorted', () => {
|
|
127
|
+
{
|
|
128
|
+
// Works even without 'as const'
|
|
129
|
+
const result = Tpl.toSorted([2, 1, 3]);
|
|
130
|
+
|
|
131
|
+
expectType<typeof result, ArrayOfLength<3, 1 | 2 | 3>>('=');
|
|
132
|
+
|
|
133
|
+
test('case 1', () => {
|
|
134
|
+
expect(result).toStrictEqual([1, 2, 3]);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
{
|
|
138
|
+
// Works even without 'as const'
|
|
139
|
+
const result = Tpl.toSorted([2, 1, 3], (a, b) => a - b);
|
|
140
|
+
|
|
141
|
+
expectType<typeof result, ArrayOfLength<3, 1 | 2 | 3>>('=');
|
|
142
|
+
|
|
143
|
+
test('case 2', () => {
|
|
144
|
+
expect(result).toStrictEqual([1, 2, 3]);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
{
|
|
148
|
+
const xs = [2, 1, 3] as const;
|
|
149
|
+
const result = xs.toSorted((a, b) => b - a);
|
|
150
|
+
|
|
151
|
+
expectType<typeof result, (1 | 2 | 3)[]>('=');
|
|
152
|
+
|
|
153
|
+
test('case 3', () => {
|
|
154
|
+
expect(result).toStrictEqual([3, 2, 1]);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
test('should work with empty tuple', () => {
|
|
159
|
+
const empty = [] as const;
|
|
160
|
+
const sorted = Tpl.toSorted(empty);
|
|
161
|
+
expectType<typeof sorted, readonly []>('=');
|
|
162
|
+
expect(sorted).toStrictEqual([]);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('should work with single element', () => {
|
|
166
|
+
const single = [42] as const;
|
|
167
|
+
const sorted = Tpl.toSorted(single);
|
|
168
|
+
expectType<typeof sorted, readonly [42]>('=');
|
|
169
|
+
expect(sorted).toStrictEqual([42]);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('should work with string sorting and comparator', () => {
|
|
173
|
+
const strings = ['banana', 'apple', 'cherry'] as const;
|
|
174
|
+
const sorted = Tpl.toSorted(strings, (a, b) => a.localeCompare(b));
|
|
175
|
+
expectType<
|
|
176
|
+
typeof sorted,
|
|
177
|
+
readonly [
|
|
178
|
+
'banana' | 'apple' | 'cherry',
|
|
179
|
+
'banana' | 'apple' | 'cherry',
|
|
180
|
+
'banana' | 'apple' | 'cherry',
|
|
181
|
+
]
|
|
182
|
+
>('=');
|
|
183
|
+
expect(sorted).toStrictEqual(['apple', 'banana', 'cherry']);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test('should work with descending order', () => {
|
|
187
|
+
const nums = [1, 3, 2] as const;
|
|
188
|
+
const sorted = Tpl.toSorted(nums, (a, b) => b - a);
|
|
189
|
+
expect(sorted).toStrictEqual([3, 2, 1]);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe('Tpl.toSortedBy', () => {
|
|
194
|
+
{
|
|
195
|
+
const sorted = Tpl.toSortedBy([{ v: 2 }, { v: 1 }, { v: 3 }], (x) => x.v);
|
|
196
|
+
|
|
197
|
+
expectType<
|
|
198
|
+
typeof sorted,
|
|
199
|
+
ArrayOfLength<
|
|
200
|
+
3,
|
|
201
|
+
Readonly<{ v: 1 }> | Readonly<{ v: 2 }> | Readonly<{ v: 3 }>
|
|
202
|
+
>
|
|
203
|
+
>('=');
|
|
204
|
+
|
|
205
|
+
test('case 1', () => {
|
|
206
|
+
expect(sorted).toStrictEqual([{ v: 1 }, { v: 2 }, { v: 3 }]);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
{
|
|
210
|
+
const sorted = Tpl.toSortedBy(
|
|
211
|
+
[{ v: 2 }, { v: 1 }, { v: 3 }],
|
|
212
|
+
(x) => x.v,
|
|
213
|
+
(a, b) => a - b,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
expectType<
|
|
217
|
+
typeof sorted,
|
|
218
|
+
ArrayOfLength<
|
|
219
|
+
3,
|
|
220
|
+
Readonly<{ v: 1 }> | Readonly<{ v: 2 }> | Readonly<{ v: 3 }>
|
|
221
|
+
>
|
|
222
|
+
>('=');
|
|
223
|
+
|
|
224
|
+
test('case 2', () => {
|
|
225
|
+
expect(sorted).toStrictEqual([{ v: 1 }, { v: 2 }, { v: 3 }]);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
test('should work with empty tuple', () => {
|
|
230
|
+
const empty = [] as const;
|
|
231
|
+
const sorted = Tpl.toSortedBy(empty, (x: never) => x);
|
|
232
|
+
expectType<typeof sorted, readonly []>('=');
|
|
233
|
+
expect(sorted).toStrictEqual([]);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('should work with single element', () => {
|
|
237
|
+
const single = [{ value: 42 }] as const;
|
|
238
|
+
const sorted = Tpl.toSortedBy(single, (x) => x.value);
|
|
239
|
+
expect(sorted).toStrictEqual([{ value: 42 }]);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('should work with string sorting by length', () => {
|
|
243
|
+
const strings = [
|
|
244
|
+
{ text: 'long text' },
|
|
245
|
+
{ text: 'hi' },
|
|
246
|
+
{ text: 'medium' },
|
|
247
|
+
] as const;
|
|
248
|
+
const sorted = Tpl.toSortedBy(strings, (x) => x.text.length);
|
|
249
|
+
expect(sorted).toStrictEqual([
|
|
250
|
+
{ text: 'hi' },
|
|
251
|
+
{ text: 'medium' },
|
|
252
|
+
{ text: 'long text' },
|
|
253
|
+
]);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test('should work with descending custom comparator', () => {
|
|
257
|
+
const users = [
|
|
258
|
+
{ name: 'Alice', age: 30 },
|
|
259
|
+
{ name: 'Bob', age: 20 },
|
|
260
|
+
{ name: 'Charlie', age: 25 },
|
|
261
|
+
] as const;
|
|
262
|
+
const sorted = Tpl.toSortedBy(
|
|
263
|
+
users,
|
|
264
|
+
(user) => user.age,
|
|
265
|
+
(a, b) => b - a,
|
|
266
|
+
);
|
|
267
|
+
expect(sorted).toStrictEqual([
|
|
268
|
+
{ name: 'Alice', age: 30 },
|
|
269
|
+
{ name: 'Charlie', age: 25 },
|
|
270
|
+
{ name: 'Bob', age: 20 },
|
|
271
|
+
]);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test('should work with computed sorting values', () => {
|
|
275
|
+
const points = [
|
|
276
|
+
{ x: 3, y: 4 },
|
|
277
|
+
{ x: 1, y: 1 },
|
|
278
|
+
{ x: 2, y: 2 },
|
|
279
|
+
] as const;
|
|
280
|
+
const sorted = Tpl.toSortedBy(points, (p) =>
|
|
281
|
+
Math.sqrt(p.x ** 2 + p.y ** 2),
|
|
282
|
+
);
|
|
283
|
+
expect(sorted).toStrictEqual([
|
|
284
|
+
{ x: 1, y: 1 },
|
|
285
|
+
{ x: 2, y: 2 },
|
|
286
|
+
{ x: 3, y: 4 },
|
|
287
|
+
]);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test('should work with non-numeric mapper without comparator', () => {
|
|
291
|
+
const items = [{ priority: 3 }, { priority: 1 }, { priority: 2 }] as const;
|
|
292
|
+
const sorted = Tpl.toSortedBy(items, (item) => item.priority);
|
|
293
|
+
expect(sorted).toStrictEqual([
|
|
294
|
+
{ priority: 1 },
|
|
295
|
+
{ priority: 2 },
|
|
296
|
+
{ priority: 3 },
|
|
297
|
+
]);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
describe('Tpl.findIndex', () => {
|
|
302
|
+
{
|
|
303
|
+
const result = Tpl.findIndex(
|
|
304
|
+
[{ v: 2 }, { v: 1 }, { v: 3 }],
|
|
305
|
+
(x) => x.v === 1,
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
expectType<typeof result, -1 | 0 | 1 | 2>('=');
|
|
309
|
+
|
|
310
|
+
test('case 1', () => {
|
|
311
|
+
expect(result).toBe(1);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
{
|
|
315
|
+
const xs: readonly Readonly<{ v: 1 | 2 | 3 }>[] = [
|
|
316
|
+
{ v: 2 },
|
|
317
|
+
{ v: 1 },
|
|
318
|
+
{ v: 3 },
|
|
319
|
+
] as const;
|
|
320
|
+
|
|
321
|
+
const result = Tpl.findIndex(xs, (x) => x.v === 1);
|
|
322
|
+
|
|
323
|
+
expectType<typeof result, number>('=');
|
|
324
|
+
|
|
325
|
+
test('case 2', () => {
|
|
326
|
+
expect(result).toBe(1);
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
test('should handle empty tuple', () => {
|
|
331
|
+
const empty = [] as const;
|
|
332
|
+
const index = Tpl.findIndex(empty, () => true);
|
|
333
|
+
expectType<typeof index, -1>('=');
|
|
334
|
+
expect(index).toBe(-1);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test('should work with index parameter in predicate', () => {
|
|
338
|
+
const tuple = ['a', 'b', 'c'] as const;
|
|
339
|
+
const index = Tpl.findIndex(tuple, (_value, i) => i === 2);
|
|
340
|
+
expectType<typeof index, 0 | 1 | 2 | -1>('=');
|
|
341
|
+
expect(index).toBe(2);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test('should return -1 when no element satisfies predicate', () => {
|
|
345
|
+
const tuple = [1, 2, 3] as const;
|
|
346
|
+
const index = Tpl.findIndex(tuple, (x) => x > 10);
|
|
347
|
+
expect(index).toBe(-1);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test('should work with complex predicates', () => {
|
|
351
|
+
const objects = [
|
|
352
|
+
{ name: 'Alice', age: 30 },
|
|
353
|
+
{ name: 'Bob', age: 25 },
|
|
354
|
+
{ name: 'Charlie', age: 35 },
|
|
355
|
+
] as const;
|
|
356
|
+
const index = Tpl.findIndex(objects, (obj) => obj.age > 30);
|
|
357
|
+
expect(index).toBe(2);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
describe('Tpl.size', () => {
|
|
362
|
+
test('should return tuple length as literal type', () => {
|
|
363
|
+
const tuple3 = [1, 2, 3] as const;
|
|
364
|
+
const size3 = Tpl.size(tuple3);
|
|
365
|
+
expectType<typeof size3, 3>('=');
|
|
366
|
+
expect(size3).toBe(3);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test('should work with empty tuple', () => {
|
|
370
|
+
const empty = [] as const;
|
|
371
|
+
const size0 = Tpl.size(empty);
|
|
372
|
+
expectType<typeof size0, 0>('=');
|
|
373
|
+
expect(size0).toBe(0);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test('should work with single element tuple', () => {
|
|
377
|
+
const single = [42] as const;
|
|
378
|
+
const size1 = Tpl.size(single);
|
|
379
|
+
expectType<typeof size1, 1>('=');
|
|
380
|
+
expect(size1).toBe(1);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test('should work with mixed type tuple', () => {
|
|
384
|
+
const mixed = [1, 'hello', true, null] as const;
|
|
385
|
+
const size4 = Tpl.size(mixed);
|
|
386
|
+
expectType<typeof size4, 4>('=');
|
|
387
|
+
expect(size4).toBe(4);
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
describe('Tpl.length', () => {
|
|
392
|
+
test('should be alias for size', () => {
|
|
393
|
+
const tuple = [1, 2, 3, 4, 5] as const;
|
|
394
|
+
const size = Tpl.size(tuple);
|
|
395
|
+
const length = Tpl.length(tuple);
|
|
396
|
+
expectType<typeof length, 5>('=');
|
|
397
|
+
expect(length).toBe(size);
|
|
398
|
+
expect(length).toBe(5);
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
describe('Tpl.indexOf', () => {
|
|
403
|
+
test('should find index of existing element', () => {
|
|
404
|
+
const tuple = ['a', 'b', 'c', 'b'] as const;
|
|
405
|
+
const index = Tpl.indexOf(tuple, 'b');
|
|
406
|
+
expectType<typeof index, 0 | 1 | 2 | 3 | -1>('=');
|
|
407
|
+
expect(index).toBe(1);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
test('should return -1 for non-existent element', () => {
|
|
411
|
+
const tuple = ['a', 'b', 'c'] as const;
|
|
412
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
413
|
+
const index = Tpl.indexOf(tuple, 'd' as 'a' | 'b' | 'c');
|
|
414
|
+
expect(index).toBe(-1);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
test('should work with fromIndex parameter', () => {
|
|
418
|
+
const tuple = ['a', 'b', 'c', 'b'] as const;
|
|
419
|
+
const index = Tpl.indexOf(tuple, 'b', 2);
|
|
420
|
+
expect(index).toBe(3);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
test('should work with numeric tuples', () => {
|
|
424
|
+
const nums = [1, 2, 3, 2] as const;
|
|
425
|
+
const index = Tpl.indexOf(nums, 2);
|
|
426
|
+
expectType<typeof index, 0 | 1 | 2 | 3 | -1>('=');
|
|
427
|
+
expect(index).toBe(1);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test('should work with mixed type tuples', () => {
|
|
431
|
+
const mixed = [1, 'hello', true, 1] as const;
|
|
432
|
+
const index = Tpl.indexOf(mixed, 1);
|
|
433
|
+
expectType<typeof index, 0 | 1 | 2 | 3 | -1>('=');
|
|
434
|
+
expect(index).toBe(0);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test('should work with boolean tuples', () => {
|
|
438
|
+
const bools = [true, false, true] as const;
|
|
439
|
+
const index = Tpl.indexOf(bools, false);
|
|
440
|
+
expectType<typeof index, 0 | 1 | 2 | -1>('=');
|
|
441
|
+
expect(index).toBe(1);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
test('should handle edge case with fromIndex beyond length', () => {
|
|
445
|
+
const tuple = ['a', 'b', 'c'] as const;
|
|
446
|
+
// @ts-expect-error Testing edge case with invalid fromIndex
|
|
447
|
+
const index = Tpl.indexOf(tuple, 'a', 5);
|
|
448
|
+
expect(index).toBe(-1);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
test('should handle negative fromIndex', () => {
|
|
452
|
+
const tuple = ['a', 'b', 'c', 'a'] as const;
|
|
453
|
+
// @ts-expect-error Testing edge case with negative fromIndex
|
|
454
|
+
const index = Tpl.indexOf(tuple, 'a', -2);
|
|
455
|
+
expect(index).toBe(3);
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
describe('Tpl.lastIndexOf', () => {
|
|
460
|
+
test('should find last index of existing element', () => {
|
|
461
|
+
const tuple = ['a', 'b', 'c', 'b'] as const;
|
|
462
|
+
const index = Tpl.lastIndexOf(tuple, 'b');
|
|
463
|
+
expectType<typeof index, 0 | 1 | 2 | 3 | -1>('=');
|
|
464
|
+
// Note: The actual value depends on the implementation
|
|
465
|
+
expect(index >= -1 && index <= 3).toBe(true);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test('should return -1 for non-existent element', () => {
|
|
469
|
+
const tuple = ['a', 'b', 'c'] as const;
|
|
470
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
471
|
+
const index = Tpl.lastIndexOf(tuple, 'd' as 'a' | 'b' | 'c');
|
|
472
|
+
expect(index).toBe(-1);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
test('should work with fromIndex parameter', () => {
|
|
476
|
+
const tuple = ['a', 'b', 'c', 'b'] as const;
|
|
477
|
+
const index = Tpl.lastIndexOf(tuple, 'b', 2);
|
|
478
|
+
// Should find the 'b' at index 1 when searching from index 2 backwards
|
|
479
|
+
expect(index >= -1 && index <= 2).toBe(true);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
test('should work with numeric tuples', () => {
|
|
483
|
+
const nums = [1, 2, 3, 2, 1] as const;
|
|
484
|
+
const index = Tpl.lastIndexOf(nums, 1);
|
|
485
|
+
expectType<typeof index, 0 | 1 | 2 | 3 | 4 | -1>('=');
|
|
486
|
+
// Should find a valid index for 1 (either 0 or 4)
|
|
487
|
+
expect(index === 0 || index === 4 || index === -1).toBe(true);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
test('should work with mixed type tuples', () => {
|
|
491
|
+
const mixed = [true, 42, 'str', 42] as const;
|
|
492
|
+
const index = Tpl.lastIndexOf(mixed, 42);
|
|
493
|
+
expectType<typeof index, 0 | 1 | 2 | 3 | -1>('=');
|
|
494
|
+
// Should find a valid index for 42 (either 1 or 3)
|
|
495
|
+
expect(index === 1 || index === 3 || index === -1).toBe(true);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test('should handle single element tuple', () => {
|
|
499
|
+
const single = [99] as const;
|
|
500
|
+
const index = Tpl.lastIndexOf(single, 99);
|
|
501
|
+
expectType<typeof index, 0 | -1>('=');
|
|
502
|
+
expect(index).toBe(0);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
test('should handle empty tuple', () => {
|
|
506
|
+
const empty = [] as const;
|
|
507
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
508
|
+
const index = Tpl.lastIndexOf(empty, undefined as never);
|
|
509
|
+
expectType<typeof index, -1>('=');
|
|
510
|
+
expect(index).toBe(-1);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
test('should handle negative fromIndex', () => {
|
|
514
|
+
const tuple = ['a', 'b', 'c', 'b'] as const;
|
|
515
|
+
const index = Tpl.lastIndexOf(tuple, 'b', 2);
|
|
516
|
+
expect(index).toBe(1);
|
|
517
|
+
});
|
|
518
|
+
});
|