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.
Files changed (143) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +534 -0
  3. package/package.json +101 -0
  4. package/src/array/array-utils-creation.test.mts +443 -0
  5. package/src/array/array-utils-modification.test.mts +197 -0
  6. package/src/array/array-utils-overload-type-error.test.mts +149 -0
  7. package/src/array/array-utils-reducing-value.test.mts +425 -0
  8. package/src/array/array-utils-search.test.mts +169 -0
  9. package/src/array/array-utils-set-op.test.mts +335 -0
  10. package/src/array/array-utils-slice-clamped.test.mts +113 -0
  11. package/src/array/array-utils-slicing.test.mts +316 -0
  12. package/src/array/array-utils-transformation.test.mts +790 -0
  13. package/src/array/array-utils-validation.test.mts +492 -0
  14. package/src/array/array-utils.mts +4000 -0
  15. package/src/array/array.test.mts +146 -0
  16. package/src/array/index.mts +2 -0
  17. package/src/array/tuple-utils.mts +519 -0
  18. package/src/array/tuple-utils.test.mts +518 -0
  19. package/src/collections/imap-mapped.mts +801 -0
  20. package/src/collections/imap-mapped.test.mts +860 -0
  21. package/src/collections/imap.mts +651 -0
  22. package/src/collections/imap.test.mts +932 -0
  23. package/src/collections/index.mts +6 -0
  24. package/src/collections/iset-mapped.mts +889 -0
  25. package/src/collections/iset-mapped.test.mts +1187 -0
  26. package/src/collections/iset.mts +682 -0
  27. package/src/collections/iset.test.mts +1084 -0
  28. package/src/collections/queue.mts +390 -0
  29. package/src/collections/queue.test.mts +282 -0
  30. package/src/collections/stack.mts +423 -0
  31. package/src/collections/stack.test.mts +225 -0
  32. package/src/expect-type.mts +206 -0
  33. package/src/functional/index.mts +4 -0
  34. package/src/functional/match.mts +300 -0
  35. package/src/functional/match.test.mts +177 -0
  36. package/src/functional/optional.mts +733 -0
  37. package/src/functional/optional.test.mts +619 -0
  38. package/src/functional/pipe.mts +212 -0
  39. package/src/functional/pipe.test.mts +85 -0
  40. package/src/functional/result.mts +1134 -0
  41. package/src/functional/result.test.mts +777 -0
  42. package/src/globals.d.mts +38 -0
  43. package/src/guard/has-key.mts +119 -0
  44. package/src/guard/has-key.test.mts +219 -0
  45. package/src/guard/index.mts +7 -0
  46. package/src/guard/is-non-empty-string.mts +108 -0
  47. package/src/guard/is-non-empty-string.test.mts +91 -0
  48. package/src/guard/is-non-null-object.mts +106 -0
  49. package/src/guard/is-non-null-object.test.mts +90 -0
  50. package/src/guard/is-primitive.mts +165 -0
  51. package/src/guard/is-primitive.test.mts +102 -0
  52. package/src/guard/is-record.mts +153 -0
  53. package/src/guard/is-record.test.mts +112 -0
  54. package/src/guard/is-type.mts +450 -0
  55. package/src/guard/is-type.test.mts +496 -0
  56. package/src/guard/key-is-in.mts +163 -0
  57. package/src/guard/key-is-in.test.mts +19 -0
  58. package/src/index.mts +10 -0
  59. package/src/iterator/index.mts +1 -0
  60. package/src/iterator/range.mts +120 -0
  61. package/src/iterator/range.test.mts +33 -0
  62. package/src/json/index.mts +1 -0
  63. package/src/json/json.mts +711 -0
  64. package/src/json/json.test.mts +628 -0
  65. package/src/number/branded-types/finite-number.mts +354 -0
  66. package/src/number/branded-types/finite-number.test.mts +135 -0
  67. package/src/number/branded-types/index.mts +26 -0
  68. package/src/number/branded-types/int.mts +278 -0
  69. package/src/number/branded-types/int.test.mts +140 -0
  70. package/src/number/branded-types/int16.mts +192 -0
  71. package/src/number/branded-types/int16.test.mts +170 -0
  72. package/src/number/branded-types/int32.mts +193 -0
  73. package/src/number/branded-types/int32.test.mts +170 -0
  74. package/src/number/branded-types/non-negative-finite-number.mts +223 -0
  75. package/src/number/branded-types/non-negative-finite-number.test.mts +188 -0
  76. package/src/number/branded-types/non-negative-int16.mts +187 -0
  77. package/src/number/branded-types/non-negative-int16.test.mts +201 -0
  78. package/src/number/branded-types/non-negative-int32.mts +187 -0
  79. package/src/number/branded-types/non-negative-int32.test.mts +204 -0
  80. package/src/number/branded-types/non-zero-finite-number.mts +229 -0
  81. package/src/number/branded-types/non-zero-finite-number.test.mts +198 -0
  82. package/src/number/branded-types/non-zero-int.mts +167 -0
  83. package/src/number/branded-types/non-zero-int.test.mts +177 -0
  84. package/src/number/branded-types/non-zero-int16.mts +196 -0
  85. package/src/number/branded-types/non-zero-int16.test.mts +195 -0
  86. package/src/number/branded-types/non-zero-int32.mts +196 -0
  87. package/src/number/branded-types/non-zero-int32.test.mts +197 -0
  88. package/src/number/branded-types/non-zero-safe-int.mts +196 -0
  89. package/src/number/branded-types/non-zero-safe-int.test.mts +232 -0
  90. package/src/number/branded-types/non-zero-uint16.mts +189 -0
  91. package/src/number/branded-types/non-zero-uint16.test.mts +199 -0
  92. package/src/number/branded-types/non-zero-uint32.mts +189 -0
  93. package/src/number/branded-types/non-zero-uint32.test.mts +199 -0
  94. package/src/number/branded-types/positive-finite-number.mts +241 -0
  95. package/src/number/branded-types/positive-finite-number.test.mts +204 -0
  96. package/src/number/branded-types/positive-int.mts +304 -0
  97. package/src/number/branded-types/positive-int.test.mts +176 -0
  98. package/src/number/branded-types/positive-int16.mts +188 -0
  99. package/src/number/branded-types/positive-int16.test.mts +197 -0
  100. package/src/number/branded-types/positive-int32.mts +188 -0
  101. package/src/number/branded-types/positive-int32.test.mts +197 -0
  102. package/src/number/branded-types/positive-safe-int.mts +187 -0
  103. package/src/number/branded-types/positive-safe-int.test.mts +210 -0
  104. package/src/number/branded-types/positive-uint16.mts +188 -0
  105. package/src/number/branded-types/positive-uint16.test.mts +203 -0
  106. package/src/number/branded-types/positive-uint32.mts +188 -0
  107. package/src/number/branded-types/positive-uint32.test.mts +203 -0
  108. package/src/number/branded-types/safe-int.mts +291 -0
  109. package/src/number/branded-types/safe-int.test.mts +170 -0
  110. package/src/number/branded-types/safe-uint.mts +187 -0
  111. package/src/number/branded-types/safe-uint.test.mts +176 -0
  112. package/src/number/branded-types/uint.mts +179 -0
  113. package/src/number/branded-types/uint.test.mts +158 -0
  114. package/src/number/branded-types/uint16.mts +186 -0
  115. package/src/number/branded-types/uint16.test.mts +170 -0
  116. package/src/number/branded-types/uint32.mts +218 -0
  117. package/src/number/branded-types/uint32.test.mts +170 -0
  118. package/src/number/enum/index.mts +2 -0
  119. package/src/number/enum/int8.mts +344 -0
  120. package/src/number/enum/int8.test.mts +180 -0
  121. package/src/number/enum/uint8.mts +293 -0
  122. package/src/number/enum/uint8.test.mts +164 -0
  123. package/src/number/index.mts +4 -0
  124. package/src/number/num.mts +604 -0
  125. package/src/number/num.test.mts +242 -0
  126. package/src/number/refined-number-utils.mts +566 -0
  127. package/src/object/index.mts +1 -0
  128. package/src/object/object.mts +447 -0
  129. package/src/object/object.test.mts +124 -0
  130. package/src/others/cast-mutable.mts +113 -0
  131. package/src/others/cast-readonly.mts +192 -0
  132. package/src/others/cast-readonly.test.mts +89 -0
  133. package/src/others/if-then.mts +98 -0
  134. package/src/others/if-then.test.mts +75 -0
  135. package/src/others/index.mts +7 -0
  136. package/src/others/map-nullable.mts +172 -0
  137. package/src/others/map-nullable.test.mts +297 -0
  138. package/src/others/memoize-function.mts +196 -0
  139. package/src/others/memoize-function.test.mts +168 -0
  140. package/src/others/tuple.mts +160 -0
  141. package/src/others/tuple.test.mts +11 -0
  142. package/src/others/unknown-to-string.mts +215 -0
  143. package/src/others/unknown-to-string.test.mts +114 -0
@@ -0,0 +1,212 @@
1
+ import { Optional } from './optional.mjs';
2
+
3
+ /**
4
+ * @internal
5
+ * Utility type to merge intersection types into a single object type.
6
+ * This helps with TypeScript's display of complex intersection types.
7
+ * @template T The intersection type to merge.
8
+ */
9
+ type MergeIntersection<T> = {
10
+ [K in keyof T]: T[K];
11
+ };
12
+
13
+ /**
14
+ * Creates a new pipe object that allows for chaining operations on a value.
15
+ *
16
+ * This function provides a fluent interface for applying transformations to values,
17
+ * with intelligent method selection based on the input type:
18
+ * - For `Optional` values: Provides `mapOptional` for safe Optional transformations
19
+ * - For other values: Provides `mapNullable` for null-safe transformations
20
+ * - All types get the basic `map` method for general transformations
21
+ *
22
+ * The pipe maintains type safety throughout the chain, automatically selecting
23
+ * the appropriate overload based on the current value type.
24
+ *
25
+ * @template A The type of the initial value to wrap in a pipe.
26
+ * @param a The initial value to wrap in a pipe.
27
+ * @returns A pipe object with chaining methods appropriate for the value type.
28
+ *
29
+ * @example
30
+ * Basic value transformation chaining:
31
+ * ```typescript
32
+ * // Simple sequential transformations
33
+ * const result = pipe(10)
34
+ * .map(x => x * 2) // 20
35
+ * .map(x => x + 5) // 25
36
+ * .map(x => x.toString()) // '25'
37
+ * .value;
38
+ *
39
+ * // String processing pipeline
40
+ * const processed = pipe(" Hello World ")
41
+ * .map(s => s.trim()) // "Hello World"
42
+ * .map(s => s.toLowerCase()) // "hello world"
43
+ * .map(s => s.split(' ')) // ["hello", "world"]
44
+ * .map(arr => arr.join('-')) // "hello-world"
45
+ * .value;
46
+ * ```
47
+ *
48
+ * @example
49
+ * Nullable value handling with automatic null checking:
50
+ * ```typescript
51
+ * // Safe operations on potentially null values
52
+ * const maybeNumber: number | null = getNumberFromAPI();
53
+ * const result = pipe(maybeNumber)
54
+ * .mapNullable(x => x * 2) // Only applies if not null
55
+ * .mapNullable(x => `Result: ${x}`) // Only applies if previous step succeeded
56
+ * .value; // 'Result: 20' or undefined
57
+ *
58
+ * // Handling undefined values
59
+ * const maybeUser: User | undefined = findUser(id);
60
+ * const userName = pipe(maybeUser)
61
+ * .mapNullable(user => user.name)
62
+ * .mapNullable(name => name.toUpperCase())
63
+ * .value; // string or undefined
64
+ * ```
65
+ *
66
+ * @example
67
+ * Optional value handling with monadic operations:
68
+ * ```typescript
69
+ * // Working with Optional types
70
+ * const optional = Optional.some(42);
71
+ * const result = pipe(optional)
72
+ * .mapOptional(x => x / 2) // Optional.some(21)
73
+ * .mapOptional(x => Math.sqrt(x)) // Optional.some(~4.58)
74
+ * .value; // Optional.some(4.58...)
75
+ *
76
+ * // Optional chains that can become None
77
+ * const parseAndProcess = (input: string) =>
78
+ * pipe(Optional.fromNullable(input))
79
+ * .mapOptional(s => s.trim())
80
+ * .mapOptional(s => s.length > 0 ? s : null) // Could become None
81
+ * .mapOptional(s => parseInt(s, 10))
82
+ * .mapOptional(n => isNaN(n) ? null : n)
83
+ * .value; // Optional<number>
84
+ * ```
85
+ *
86
+ * @example
87
+ * Mixed type transformations:
88
+ * ```typescript
89
+ * // Starting with a string, transforming through different types
90
+ * const complex = pipe('hello')
91
+ * .map(s => s.length) // number: 5
92
+ * .map(n => n > 3 ? n : null) // number | null: 5
93
+ * .mapNullable(n => n * 10) // number: 50 (or undefined if null)
94
+ * .value; // 50 or undefined
95
+ *
96
+ * // API response processing
97
+ * const processApiResponse = (response: ApiResponse) =>
98
+ * pipe(response)
99
+ * .map(r => r.data) // Extract data
100
+ * .mapNullable(data => data.user) // Safe user access
101
+ * .mapNullable(user => user.profile) // Safe profile access
102
+ * .mapNullable(profile => profile.avatar) // Safe avatar access
103
+ * .value; // string | undefined
104
+ * ```
105
+ *
106
+ * @example
107
+ * Error-safe computation chains:
108
+ * ```typescript
109
+ * // Building complex computations safely
110
+ * const safeCalculation = (input: unknown) =>
111
+ * pipe(input)
112
+ * .map(val => typeof val === 'number' ? val : null)
113
+ * .mapNullable(n => n > 0 ? n : null) // Positive numbers only
114
+ * .mapNullable(n => Math.sqrt(n)) // Safe square root
115
+ * .mapNullable(n => n < 100 ? n : null) // Limit result
116
+ * .mapNullable(n => Math.round(n * 100) / 100) // Round to 2 decimals
117
+ * .value; // number | undefined
118
+ * ```
119
+ */
120
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
121
+ export const pipe: PipeFnOverload = (<const A,>(a: A) => {
122
+ if (Optional.isOptional(a)) {
123
+ return {
124
+ value: a,
125
+ map: (fn) => pipe(fn(a)),
126
+ mapOptional: (fn) => pipe(Optional.map(a, fn)),
127
+ } satisfies PipeWithMapOptional<Optional.Base>;
128
+ } else {
129
+ return {
130
+ value: a,
131
+ map: (fn) => pipe(fn(a)),
132
+ mapNullable: (fn) => pipe(a == null ? undefined : fn(a)),
133
+ } satisfies PipeWithMapNullable<A>;
134
+ }
135
+ }) as PipeFnOverload;
136
+
137
+ /**
138
+ * @internal
139
+ * Overloaded function type for the pipe function.
140
+ * Automatically selects the appropriate pipe type based on input:
141
+ * - Optional types get PipeWithMapOptional
142
+ * - All other types get PipeWithMapNullable
143
+ * @template A The type of value being piped.
144
+ */
145
+ type PipeFnOverload = {
146
+ /** Creates a pipe for Optional values with mapOptional support. */
147
+ <const A extends Optional.Base>(a: A): PipeWithMapOptional<A>;
148
+ /** Creates a pipe for any other value type with mapNullable support. */
149
+ <const A>(a: A): PipeWithMapNullable<A>;
150
+ };
151
+
152
+ /**
153
+ * @internal
154
+ * Base pipe interface providing core functionality.
155
+ * All pipe types extend this interface.
156
+ * @template A The type of the current value in the pipe.
157
+ */
158
+ type PipeBase<A> = Readonly<{
159
+ /** The current value being piped. */
160
+ value: A;
161
+ /**
162
+ * Maps the current value to a new value using the provided function.
163
+ * @template B The type of the new value.
164
+ * @param fn Function to transform the current value.
165
+ * @returns A new pipe containing the transformed value.
166
+ */
167
+ map: <B>(fn: (a: A) => B) => PipeBase<B>;
168
+ }>;
169
+
170
+ /**
171
+ * @internal
172
+ * Pipe interface for non-Optional values, providing null-safe mapping.
173
+ * Extends PipeBase with mapNullable functionality.
174
+ * @template A The type of the current value in the pipe.
175
+ */
176
+ type PipeWithMapNullable<A> = MergeIntersection<
177
+ PipeBase<A> &
178
+ Readonly<{
179
+ /**
180
+ * Maps the current value only if it's not null or undefined.
181
+ * If the current value is null/undefined, the transformation is skipped
182
+ * and undefined is propagated through the pipe.
183
+ * @template B The type of the transformed value.
184
+ * @param fn Function to transform the non-null value.
185
+ * @returns A new pipe containing the transformed value or undefined.
186
+ */
187
+ mapNullable: <B>(fn: (a: NonNullable<A>) => B) => PipeBase<B | undefined>;
188
+ }>
189
+ >;
190
+
191
+ /**
192
+ * @internal
193
+ * Pipe interface for Optional values, providing Optional-aware mapping.
194
+ * Extends PipeBase with mapOptional functionality for monadic operations.
195
+ * @template A The Optional type currently in the pipe.
196
+ */
197
+ type PipeWithMapOptional<A extends Optional.Base> = MergeIntersection<
198
+ PipeBase<A> &
199
+ Readonly<{
200
+ /**
201
+ * Maps the value inside an Optional using Optional.map semantics.
202
+ * If the Optional is None, the transformation is skipped and None is propagated.
203
+ * If the Optional is Some, the transformation is applied to the inner value.
204
+ * @template B The type of the transformed inner value.
205
+ * @param fn Function to transform the inner value of the Optional.
206
+ * @returns A new pipe containing an Optional with the transformed value.
207
+ */
208
+ mapOptional: <B>(
209
+ fn: (a: Optional.Unwrap<A>) => B,
210
+ ) => PipeBase<Optional<B>>;
211
+ }>
212
+ >;
@@ -0,0 +1,85 @@
1
+ import { expectType } from '../expect-type.mjs';
2
+ import { Optional } from './optional.mjs';
3
+ import { pipe } from './pipe.mjs';
4
+
5
+ describe('pipe', () => {
6
+ test('basic pipe operations', () => {
7
+ const result = pipe(5)
8
+ .map((x) => x * 2)
9
+ .map((x) => x + 1).value;
10
+
11
+ expect(result).toBe(11);
12
+ expectType<typeof result, number>('=');
13
+ });
14
+
15
+ test('pipe with string operations', () => {
16
+ const result = pipe('hello')
17
+ .map((s) => s.toUpperCase())
18
+ .map((s) => `${s}!`).value;
19
+
20
+ expect(result).toBe('HELLO!');
21
+ expectType<typeof result, string>('=');
22
+ });
23
+
24
+ test('pipe with array operations', () => {
25
+ const result = pipe([1, 2, 3])
26
+ .map((arr) => arr.map((x) => x * 2))
27
+ .map((arr) => arr.length).value;
28
+
29
+ expect(result).toBe(3);
30
+ expectType<typeof result, number>('=');
31
+ });
32
+
33
+ test('mapNullable with non-null value', () => {
34
+ const result = pipe(5 as number | null).mapNullable((x) => x * 2).value;
35
+
36
+ expect(result).toBe(10);
37
+ expectType<typeof result, number | undefined>('=');
38
+ });
39
+
40
+ test('mapNullable with null value', () => {
41
+ const result = pipe(null as number | null).mapNullable((x) => x * 2).value;
42
+
43
+ expect(result).toBeUndefined();
44
+ expectType<typeof result, number | undefined>('=');
45
+ });
46
+
47
+ test('mapNullable with undefined value', () => {
48
+ const result = pipe(undefined as number | undefined).mapNullable(
49
+ (x) => x * 2,
50
+ ).value;
51
+
52
+ expect(result).toBeUndefined();
53
+ expectType<typeof result, number | undefined>('=');
54
+ });
55
+
56
+ test('mapOptional with Some value', () => {
57
+ const optional = Optional.some(42);
58
+ const result = pipe(optional).mapOptional((x) => x * 2).value;
59
+
60
+ expect(Optional.isSome(result)).toBe(true);
61
+ if (Optional.isSome(result)) {
62
+ expect(result.value).toBe(84);
63
+ }
64
+ expectType<typeof result, Optional<number>>('=');
65
+ });
66
+
67
+ test('mapOptional with None value', () => {
68
+ const optional: Optional<number> = Optional.none;
69
+ const result = pipe(optional).mapOptional((x) => x * 2).value;
70
+
71
+ expect(Optional.isNone(result)).toBe(true);
72
+ expectType<typeof result, Optional<number>>('=');
73
+ });
74
+
75
+ test('chaining multiple operations', () => {
76
+ const result = pipe('5')
77
+ .map((s) => Number.parseInt(s, 10))
78
+ .map((n) => n * 3)
79
+ .map((n) => n.toString())
80
+ .map((s) => `${s} items`).value;
81
+
82
+ expect(result).toBe('15 items');
83
+ expectType<typeof result, string>('=');
84
+ });
85
+ });