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,149 @@
|
|
|
1
|
+
import { expectType } from '../expect-type.mjs';
|
|
2
|
+
import { pipe } from '../functional/index.mjs';
|
|
3
|
+
import { Arr } from './array-utils.mjs';
|
|
4
|
+
|
|
5
|
+
// Tests for ensuring type errors occur with incorrect usage of overloaded functions
|
|
6
|
+
|
|
7
|
+
describe('Array overloaded functions - type error validation', () => {
|
|
8
|
+
const testArray = [1, 2, 3, 4, 5] as const;
|
|
9
|
+
const predicate = (x: number): boolean => x > 3;
|
|
10
|
+
|
|
11
|
+
describe('Arr.findIndex type safety', () => {
|
|
12
|
+
test('findIndex with correct arguments should work', () => {
|
|
13
|
+
// These should work fine
|
|
14
|
+
const _result1 = Arr.findIndex(testArray, predicate);
|
|
15
|
+
const _result2 = Arr.findIndex(predicate);
|
|
16
|
+
const _result3 = Arr.findIndex(predicate)(testArray);
|
|
17
|
+
|
|
18
|
+
expectType<typeof _result1, SizeType.Arr | -1>('=');
|
|
19
|
+
expectType<
|
|
20
|
+
typeof _result2,
|
|
21
|
+
(array: readonly number[]) => SizeType.Arr | -1
|
|
22
|
+
>('=');
|
|
23
|
+
expectType<typeof _result3, SizeType.Arr | -1>('=');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('findIndex type constraints work correctly', () => {
|
|
27
|
+
// Test that type constraints exist for predicate function
|
|
28
|
+
// @ts-expect-error - Predicate should return boolean, not string
|
|
29
|
+
Arr.findIndex(testArray, (x: number) => x.toString());
|
|
30
|
+
|
|
31
|
+
// @ts-expect-error - Array element type should match predicate parameter
|
|
32
|
+
Arr.findIndex(['a', 'b', 'c'], (x: number) => x > 0);
|
|
33
|
+
|
|
34
|
+
expect(true).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('Arr.filterNot type safety', () => {
|
|
39
|
+
test('filterNot with correct arguments should work', () => {
|
|
40
|
+
// These should work fine
|
|
41
|
+
const _result1 = Arr.filterNot(testArray, predicate);
|
|
42
|
+
const _result2 = Arr.filterNot(predicate);
|
|
43
|
+
const _result3 = Arr.filterNot(predicate)(testArray);
|
|
44
|
+
|
|
45
|
+
expectType<typeof _result1, readonly number[]>('<=');
|
|
46
|
+
expectType<
|
|
47
|
+
typeof _result2,
|
|
48
|
+
(array: readonly number[]) => readonly number[]
|
|
49
|
+
>('<=');
|
|
50
|
+
expectType<typeof _result3, readonly number[]>('<=');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('filterNot type constraints work correctly', () => {
|
|
54
|
+
// @ts-expect-error - Predicate should return boolean, not string
|
|
55
|
+
Arr.filterNot(testArray, (x: number) => x.toString());
|
|
56
|
+
|
|
57
|
+
// @ts-expect-error - Array element type should match predicate parameter
|
|
58
|
+
Arr.filterNot(['a', 'b', 'c'], (x: number) => x > 0);
|
|
59
|
+
|
|
60
|
+
expect(true).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('Arr.partition type safety', () => {
|
|
65
|
+
test('partition with correct arguments should work', () => {
|
|
66
|
+
// These should work fine
|
|
67
|
+
const _result1 = Arr.partition(testArray, 2);
|
|
68
|
+
const _result2 = Arr.partition(2);
|
|
69
|
+
const _result3 = Arr.partition(2)(testArray);
|
|
70
|
+
|
|
71
|
+
expectType<typeof _result1, readonly (readonly number[])[]>('<=');
|
|
72
|
+
expectType<
|
|
73
|
+
typeof _result2,
|
|
74
|
+
<A>(array: readonly A[]) => readonly (readonly A[])[]
|
|
75
|
+
>('<=');
|
|
76
|
+
expectType<typeof _result3, readonly (readonly number[])[]>('<=');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('partition type constraints work correctly', () => {
|
|
80
|
+
// @ts-expect-error - Partition size should be number, not string
|
|
81
|
+
expect(() => Arr.partition(testArray, 'invalid')).toThrowError();
|
|
82
|
+
|
|
83
|
+
// @ts-expect-error - Negative partition size should not be allowed
|
|
84
|
+
Arr.partition(testArray, -1);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('Arr.range type safety', () => {
|
|
89
|
+
test('range with correct arguments should work', () => {
|
|
90
|
+
// These should work fine
|
|
91
|
+
const _result1 = Arr.range(1, 5);
|
|
92
|
+
const _result2 = Arr.range(1, 5, 1);
|
|
93
|
+
|
|
94
|
+
expectType<typeof _result1, readonly [1, 2, 3, 4]>('=');
|
|
95
|
+
expectType<typeof _result2, readonly [1, 2, 3, 4]>('=');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('range type constraints work correctly', () => {
|
|
99
|
+
// @ts-expect-error - Range bounds should be numbers, not strings
|
|
100
|
+
expect(() => Arr.range('1', '5')).toThrowError();
|
|
101
|
+
|
|
102
|
+
// @ts-expect-error - Step should be number, not string
|
|
103
|
+
Arr.range(1, 5, 'invalid');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Type-safe spread operations', () => {
|
|
108
|
+
test('spread with correct tuple types should work', () => {
|
|
109
|
+
// Correct usage with spread
|
|
110
|
+
const correctArgs1 = [testArray, predicate] as const;
|
|
111
|
+
const correctArgs2 = [predicate] as const;
|
|
112
|
+
|
|
113
|
+
const _result1 = Arr.findIndex(...correctArgs1);
|
|
114
|
+
const _result2 = Arr.findIndex(...correctArgs2);
|
|
115
|
+
|
|
116
|
+
expectType<typeof _result1, SizeType.Arr | -1>('<=');
|
|
117
|
+
expectType<
|
|
118
|
+
typeof _result2,
|
|
119
|
+
(array: readonly number[]) => SizeType.Arr | -1
|
|
120
|
+
>('<=');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('spread with incorrect types should cause errors', () => {
|
|
124
|
+
const invalidArgs: [string[], (x: string) => number] = [
|
|
125
|
+
['a'],
|
|
126
|
+
(x) => x.length,
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
// @ts-expect-error - Wrong types in tuple for spread
|
|
130
|
+
Arr.findIndex(...invalidArgs);
|
|
131
|
+
|
|
132
|
+
expect(true).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('Function composition with overloaded functions', () => {
|
|
137
|
+
test('composition should preserve type safety', () => {
|
|
138
|
+
// This should work
|
|
139
|
+
const findPositive = Arr.findIndex((x: number) => x > 0);
|
|
140
|
+
const filterNegative = Arr.filterNot((x: number) => x < 0);
|
|
141
|
+
|
|
142
|
+
const _composedResult = pipe(testArray)
|
|
143
|
+
.map(filterNegative)
|
|
144
|
+
.map(findPositive).value;
|
|
145
|
+
|
|
146
|
+
expectType<typeof _composedResult, SizeType.Arr | -1>('=');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import { IMap } from '../collections/index.mjs';
|
|
2
|
+
import { expectType } from '../expect-type.mjs';
|
|
3
|
+
import { Optional, Result } from '../functional/index.mjs';
|
|
4
|
+
import { Arr } from './array-utils.mjs';
|
|
5
|
+
|
|
6
|
+
describe('Arr reducing value', () => {
|
|
7
|
+
describe('min', () => {
|
|
8
|
+
{
|
|
9
|
+
const xs = [3, 5, 4] as const;
|
|
10
|
+
const result = Arr.min(xs);
|
|
11
|
+
|
|
12
|
+
expectType<typeof result, Optional.Some<3 | 4 | 5>>('=');
|
|
13
|
+
|
|
14
|
+
test('case 1', () => {
|
|
15
|
+
expect(Optional.isSome(result)).toBe(true);
|
|
16
|
+
if (Optional.isSome(result)) {
|
|
17
|
+
expect(result.value).toBe(3);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
{
|
|
22
|
+
const xs = [3, 5, 4] as const;
|
|
23
|
+
const result = Arr.min(xs, (a, b) => a - b);
|
|
24
|
+
|
|
25
|
+
expectType<typeof result, Optional.Some<3 | 4 | 5>>('=');
|
|
26
|
+
|
|
27
|
+
test('case 2', () => {
|
|
28
|
+
expect(Optional.isSome(result)).toBe(true);
|
|
29
|
+
if (Optional.isSome(result)) {
|
|
30
|
+
expect(result.value).toBe(3);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
{
|
|
35
|
+
const xs: readonly (3 | 4 | 5)[] = [3, 5, 4] as const;
|
|
36
|
+
const result = Arr.min(xs, (a, b) => a - b);
|
|
37
|
+
|
|
38
|
+
expectType<typeof result, Optional<3 | 4 | 5>>('=');
|
|
39
|
+
|
|
40
|
+
test('case 3', () => {
|
|
41
|
+
expect(Optional.isSome(result)).toBe(true);
|
|
42
|
+
if (Optional.isSome(result)) {
|
|
43
|
+
expect(result.value).toBe(3);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('max', () => {
|
|
50
|
+
const xs = [3, 5, 4] as const;
|
|
51
|
+
const result = Arr.max(xs, (a, b) => a - b);
|
|
52
|
+
|
|
53
|
+
expectType<typeof result, Optional.Some<3 | 4 | 5>>('=');
|
|
54
|
+
|
|
55
|
+
test('case 1', () => {
|
|
56
|
+
expect(Optional.isSome(result)).toBe(true);
|
|
57
|
+
if (Optional.isSome(result)) {
|
|
58
|
+
expect(result.value).toBe(5);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('case 2: no comparator', () => {
|
|
63
|
+
const res = Arr.max(xs);
|
|
64
|
+
expectType<typeof res, Optional.Some<3 | 4 | 5>>('=');
|
|
65
|
+
expect(Optional.isSome(res)).toBe(true);
|
|
66
|
+
if (Optional.isSome(res)) {
|
|
67
|
+
expect(res.value).toBe(5);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('case 3: readonly array', () => {
|
|
72
|
+
const arr: readonly number[] = [1, 5, 2];
|
|
73
|
+
const res = Arr.max(arr);
|
|
74
|
+
expectType<typeof res, Optional<number>>('=');
|
|
75
|
+
expect(Optional.isSome(res)).toBe(true);
|
|
76
|
+
if (Optional.isSome(res)) {
|
|
77
|
+
expect(res.value).toBe(5);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('case 4: empty array', () => {
|
|
82
|
+
const arr: readonly number[] = [];
|
|
83
|
+
const res = Arr.max(arr);
|
|
84
|
+
expectType<typeof res, Optional<number>>('=');
|
|
85
|
+
expect(Optional.isNone(res)).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('minBy', () => {
|
|
90
|
+
const xs: NonEmptyArray<
|
|
91
|
+
| Readonly<{ x: 1; y: 2 }>
|
|
92
|
+
| Readonly<{ x: 2; y: 3 }>
|
|
93
|
+
| Readonly<{ x: 3; y: 2 }>
|
|
94
|
+
| Readonly<{ x: 4; y: 1 }>
|
|
95
|
+
| Readonly<{ x: 5; y: 1 }>
|
|
96
|
+
| Readonly<{ x: 6; y: 1 }>
|
|
97
|
+
> = [
|
|
98
|
+
{ x: 5, y: 1 },
|
|
99
|
+
{ x: 4, y: 1 },
|
|
100
|
+
{ x: 6, y: 1 },
|
|
101
|
+
{ x: 3, y: 2 },
|
|
102
|
+
{ x: 1, y: 2 },
|
|
103
|
+
{ x: 2, y: 3 },
|
|
104
|
+
] as const;
|
|
105
|
+
|
|
106
|
+
const result = Arr.minBy(xs, (a) => a.x);
|
|
107
|
+
|
|
108
|
+
expectType<
|
|
109
|
+
typeof result,
|
|
110
|
+
Optional.Some<
|
|
111
|
+
| Readonly<{ x: 1; y: 2 }>
|
|
112
|
+
| Readonly<{ x: 2; y: 3 }>
|
|
113
|
+
| Readonly<{ x: 3; y: 2 }>
|
|
114
|
+
| Readonly<{ x: 4; y: 1 }>
|
|
115
|
+
| Readonly<{ x: 5; y: 1 }>
|
|
116
|
+
| Readonly<{ x: 6; y: 1 }>
|
|
117
|
+
>
|
|
118
|
+
>('=');
|
|
119
|
+
|
|
120
|
+
test('case 1', () => {
|
|
121
|
+
expect(Optional.isSome(result)).toBe(true);
|
|
122
|
+
if (Optional.isSome(result)) {
|
|
123
|
+
expect(result.value).toStrictEqual({ x: 1, y: 2 });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('case 2: empty array', () => {
|
|
128
|
+
const arr: readonly { x: number }[] = [];
|
|
129
|
+
const res = Arr.minBy(arr, (a) => a.x);
|
|
130
|
+
expectType<typeof res, Optional<{ x: number }>>('=');
|
|
131
|
+
expect(Optional.isNone(res)).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('case 3: custom comparator', () => {
|
|
135
|
+
const arr = [
|
|
136
|
+
{ name: 'apple', score: 10 },
|
|
137
|
+
{ name: 'banana', score: 5 },
|
|
138
|
+
{ name: 'cherry', score: 12 },
|
|
139
|
+
] as const;
|
|
140
|
+
|
|
141
|
+
const res = Arr.minBy(
|
|
142
|
+
arr,
|
|
143
|
+
(item) => item.name,
|
|
144
|
+
(a, b) => a.localeCompare(b),
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
expectType<
|
|
148
|
+
typeof res,
|
|
149
|
+
Optional<
|
|
150
|
+
| Readonly<{ name: 'apple'; score: 10 }>
|
|
151
|
+
| Readonly<{ name: 'banana'; score: 5 }>
|
|
152
|
+
| Readonly<{ name: 'cherry'; score: 12 }>
|
|
153
|
+
>
|
|
154
|
+
>('=');
|
|
155
|
+
|
|
156
|
+
expect(Optional.isSome(res)).toBe(true);
|
|
157
|
+
if (Optional.isSome(res)) {
|
|
158
|
+
expect(res.value).toStrictEqual({ name: 'apple', score: 10 });
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('maxBy', () => {
|
|
164
|
+
const xs: NonEmptyArray<
|
|
165
|
+
| Readonly<{ x: 1; y: 2 }>
|
|
166
|
+
| Readonly<{ x: 2; y: 3 }>
|
|
167
|
+
| Readonly<{ x: 3; y: 2 }>
|
|
168
|
+
| Readonly<{ x: 4; y: 1 }>
|
|
169
|
+
| Readonly<{ x: 5; y: 1 }>
|
|
170
|
+
| Readonly<{ x: 6; y: 1 }>
|
|
171
|
+
> = [
|
|
172
|
+
{ x: 5, y: 1 },
|
|
173
|
+
{ x: 4, y: 1 },
|
|
174
|
+
{ x: 6, y: 1 },
|
|
175
|
+
{ x: 3, y: 2 },
|
|
176
|
+
{ x: 1, y: 2 },
|
|
177
|
+
{ x: 2, y: 3 },
|
|
178
|
+
] as const;
|
|
179
|
+
|
|
180
|
+
const result = Arr.maxBy(xs, (a) => a.x);
|
|
181
|
+
|
|
182
|
+
expectType<
|
|
183
|
+
typeof result,
|
|
184
|
+
Optional.Some<
|
|
185
|
+
| Readonly<{ x: 1; y: 2 }>
|
|
186
|
+
| Readonly<{ x: 2; y: 3 }>
|
|
187
|
+
| Readonly<{ x: 3; y: 2 }>
|
|
188
|
+
| Readonly<{ x: 4; y: 1 }>
|
|
189
|
+
| Readonly<{ x: 5; y: 1 }>
|
|
190
|
+
| Readonly<{ x: 6; y: 1 }>
|
|
191
|
+
>
|
|
192
|
+
>('=');
|
|
193
|
+
|
|
194
|
+
test('case 1', () => {
|
|
195
|
+
expect(Optional.isSome(result)).toBe(true);
|
|
196
|
+
if (Optional.isSome(result)) {
|
|
197
|
+
expect(result.value).toStrictEqual({ x: 6, y: 1 });
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('case 2: empty array', () => {
|
|
202
|
+
const arr: readonly { x: number }[] = [];
|
|
203
|
+
const res = Arr.maxBy(arr, (a) => a.x);
|
|
204
|
+
expectType<typeof res, Optional<{ x: number }>>('=');
|
|
205
|
+
expect(Optional.isNone(res)).toBe(true);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('case 3: custom comparator', () => {
|
|
209
|
+
const arr = [
|
|
210
|
+
{ name: 'apple', score: 10 },
|
|
211
|
+
{ name: 'banana', score: 5 },
|
|
212
|
+
{ name: 'cherry', score: 12 },
|
|
213
|
+
] as const;
|
|
214
|
+
|
|
215
|
+
const res = Arr.maxBy(
|
|
216
|
+
arr,
|
|
217
|
+
(item) => item.name,
|
|
218
|
+
(a, b) => a.localeCompare(b),
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
expectType<
|
|
222
|
+
typeof res,
|
|
223
|
+
Optional<
|
|
224
|
+
| Readonly<{ name: 'apple'; score: 10 }>
|
|
225
|
+
| Readonly<{ name: 'banana'; score: 5 }>
|
|
226
|
+
| Readonly<{ name: 'cherry'; score: 12 }>
|
|
227
|
+
>
|
|
228
|
+
>('=');
|
|
229
|
+
|
|
230
|
+
expect(Optional.isSome(res)).toBe(true);
|
|
231
|
+
if (Optional.isSome(res)) {
|
|
232
|
+
expect(res.value).toStrictEqual({ name: 'cherry', score: 12 });
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe('count', () => {
|
|
238
|
+
const xs = [
|
|
239
|
+
{ x: 1, y: 1 },
|
|
240
|
+
{ x: 2, y: 1 },
|
|
241
|
+
{ x: 3, y: 1 },
|
|
242
|
+
{ x: 1, y: 2 },
|
|
243
|
+
{ x: 2, y: 2 },
|
|
244
|
+
{ x: 1, y: 3 },
|
|
245
|
+
] as const;
|
|
246
|
+
|
|
247
|
+
const result = Arr.count(xs, (a) => a.x === 2);
|
|
248
|
+
|
|
249
|
+
expectType<typeof result, Uint32>('=');
|
|
250
|
+
|
|
251
|
+
test('case 1', () => {
|
|
252
|
+
expect(result).toBe(2);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test('case 2: empty array', () => {
|
|
256
|
+
const arr: readonly number[] = [];
|
|
257
|
+
const res = Arr.count(arr, (x) => x > 0);
|
|
258
|
+
expectType<typeof res, Uint32>('=');
|
|
259
|
+
expect(res).toBe(0);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('countBy', () => {
|
|
264
|
+
const xs = [
|
|
265
|
+
{ x: 1, y: 1 },
|
|
266
|
+
{ x: 2, y: 1 },
|
|
267
|
+
{ x: 3, y: 1 },
|
|
268
|
+
{ x: 1, y: 2 },
|
|
269
|
+
{ x: 2, y: 2 },
|
|
270
|
+
{ x: 1, y: 3 },
|
|
271
|
+
] as const;
|
|
272
|
+
|
|
273
|
+
const result = Arr.countBy(xs, (a) => a.x);
|
|
274
|
+
|
|
275
|
+
expectType<typeof result, IMap<1 | 2 | 3, Uint32>>('=');
|
|
276
|
+
|
|
277
|
+
test('case 1', () => {
|
|
278
|
+
expect(result).toStrictEqual(
|
|
279
|
+
IMap.create<1 | 2 | 3, number>([
|
|
280
|
+
[1, 3],
|
|
281
|
+
[2, 2],
|
|
282
|
+
[3, 1],
|
|
283
|
+
]),
|
|
284
|
+
);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test('case 2: empty array', () => {
|
|
288
|
+
const arr: readonly { x: number }[] = [];
|
|
289
|
+
const res = Arr.countBy(arr, (a) => a.x);
|
|
290
|
+
expectType<typeof res, IMap<number, Uint32>>('=');
|
|
291
|
+
expect(res.size).toBe(0);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe('foldl', () => {
|
|
296
|
+
test('empty array', () => {
|
|
297
|
+
const result = Arr.foldl([], (acc, curr: number) => acc + curr, 0);
|
|
298
|
+
expectType<typeof result, number>('=');
|
|
299
|
+
expect(result).toBe(0);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test('sum numbers', () => {
|
|
303
|
+
const result = Arr.foldl(
|
|
304
|
+
[1, 2, 3] as const,
|
|
305
|
+
(acc, curr) => acc + curr,
|
|
306
|
+
0,
|
|
307
|
+
);
|
|
308
|
+
expectType<typeof result, number>('=');
|
|
309
|
+
expect(result).toBe(6);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test('concatenate strings', () => {
|
|
313
|
+
const result = Arr.foldl(
|
|
314
|
+
['a', 'b', 'c'] as const,
|
|
315
|
+
(acc, curr) => acc + curr,
|
|
316
|
+
'',
|
|
317
|
+
);
|
|
318
|
+
expectType<typeof result, string>('=');
|
|
319
|
+
expect(result).toBe('abc');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('alias reduce', () => {
|
|
323
|
+
expect(Arr.reduce).toBe(Arr.foldl);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe('foldr', () => {
|
|
328
|
+
test('empty array', () => {
|
|
329
|
+
const result = Arr.foldr([], (acc, curr: number) => acc + curr, 0);
|
|
330
|
+
expectType<typeof result, number>('=');
|
|
331
|
+
expect(result).toBe(0);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test('subtract numbers from right', () => {
|
|
335
|
+
// (1 - (2 - (3 - 0))) = 1 - (2 - 3) = 1 - (-1) = 2
|
|
336
|
+
const result = Arr.foldr(
|
|
337
|
+
[1, 2, 3] as const,
|
|
338
|
+
(acc, curr) => curr - acc,
|
|
339
|
+
0,
|
|
340
|
+
);
|
|
341
|
+
expectType<typeof result, number>('=');
|
|
342
|
+
expect(result).toBe(2); // 3 - (2 - (1 - 0)) = 3 - (2 - 1) = 3 - 1 = 2. No, this is (acc, curr) => acc - curr.
|
|
343
|
+
// The callback is (previousValue: S, currentValue: A) => S
|
|
344
|
+
// So it's initialValue for S.
|
|
345
|
+
// Iteration 1: prev = 0, curr = 3. Result = 3 - 0 = 3.
|
|
346
|
+
// Iteration 2: prev = 3, curr = 2. Result = 2 - 3 = -1.
|
|
347
|
+
// Iteration 3: prev = -1, curr = 1. Result = 1 - (-1) = 2.
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test('concatenate strings from right', () => {
|
|
351
|
+
const result = Arr.foldr(
|
|
352
|
+
['a', 'b', 'c'] as const,
|
|
353
|
+
(acc, curr) => curr + acc,
|
|
354
|
+
'',
|
|
355
|
+
);
|
|
356
|
+
expectType<typeof result, string>('=');
|
|
357
|
+
expect(result).toBe('abc'); // c + (b + (a + "")) = cba. No, it's curr + acc.
|
|
358
|
+
// Iteration 1: prev = "", curr = "c". Result = "c" + "" = "c".
|
|
359
|
+
// Iteration 2: prev = "c", curr = "b". Result = "b" + "c" = "bc".
|
|
360
|
+
// Iteration 3: prev = "bc", curr = "a". Result = "a" + "bc" = "abc".
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test('alias reduceRight', () => {
|
|
364
|
+
expect(Arr.reduceRight).toBe(Arr.foldr);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
describe('sum', () => {
|
|
369
|
+
test('empty array', () => {
|
|
370
|
+
const result = Arr.sum([]);
|
|
371
|
+
expectType<typeof result, 0>('=');
|
|
372
|
+
expect(result).toBe(0);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
test('one element array', () => {
|
|
376
|
+
const result = Arr.sum([23]);
|
|
377
|
+
expectType<typeof result, 23>('=');
|
|
378
|
+
expect(result).toBe(23);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test('positive numbers', () => {
|
|
382
|
+
const result = Arr.sum([1, 2, 3, 4, 5] as const);
|
|
383
|
+
expectType<typeof result, number>('=');
|
|
384
|
+
expect(result).toBe(15);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test('mixed numbers', () => {
|
|
388
|
+
const result = Arr.sum([1, -2, 3, 0, -5] as const);
|
|
389
|
+
expectType<typeof result, number>('=');
|
|
390
|
+
expect(result).toBe(-3);
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
describe('join', () => {
|
|
395
|
+
test('should join array elements', () => {
|
|
396
|
+
const arr = ['Hello', 'World'];
|
|
397
|
+
const result = Arr.join(arr, ' ');
|
|
398
|
+
|
|
399
|
+
expect(Result.isOk(result)).toBe(true);
|
|
400
|
+
if (Result.isOk(result)) {
|
|
401
|
+
expect(result.value).toBe('Hello World');
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
test('should handle empty separator', () => {
|
|
406
|
+
const arr = ['a', 'b', 'c'];
|
|
407
|
+
const result = Arr.join(arr, '');
|
|
408
|
+
|
|
409
|
+
expect(Result.isOk(result)).toBe(true);
|
|
410
|
+
if (Result.isOk(result)) {
|
|
411
|
+
expect(result.value).toBe('abc');
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test('should handle undefined separator', () => {
|
|
416
|
+
const arr = ['a', 'b', 'c'];
|
|
417
|
+
const result = Arr.join(arr);
|
|
418
|
+
|
|
419
|
+
expect(Result.isOk(result)).toBe(true);
|
|
420
|
+
if (Result.isOk(result)) {
|
|
421
|
+
expect(result.value).toBe('a,b,c');
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
});
|