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
package/README.md ADDED
@@ -0,0 +1,534 @@
1
+ # ts-data-forge
2
+
3
+ [![npm version](https://img.shields.io/npm/v/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge)
4
+ [![npm downloads](https://img.shields.io/npm/dm/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge)
5
+ [![License](https://img.shields.io/npm/l/ts-data-forge.svg)](./LICENSE)
6
+ [![codecov](https://codecov.io/gh/noshiro-pf/ts-data-forge/graph/badge.svg?token=69TA40HACZ)](https://codecov.io/gh/noshiro-pf/ts-data-forge)
7
+
8
+ **ts-data-forge** is a TypeScript utility library that provides type-safe functional programming utilities with zero runtime dependencies. It aims to enhance development robustness, maintainability, and correctness by leveraging TypeScript's powerful type system.
9
+
10
+ ## Features
11
+
12
+ This library offers a range of utilities, including:
13
+
14
+ - **Compile-Time Type Checking**: Assert type relationships at compile time with `expectType`.
15
+ - **Immutable Collections**: Type-safe and immutable map (`IMap`), set (`ISet`) implementations.
16
+ - **Array Utilities**: A comprehensive suite of functions for array manipulation, creation, transformation, and querying.
17
+ - **Number Utilities**: Safe and convenient functions for numerical operations, including branded types and range checking.
18
+ - **Object Utilities**: Helpers for working with objects, such as shallow equality checks.
19
+ - **Functional Programming Tools**: Utilities like `pipe`, `Optional`, and `Result` to support functional programming patterns.
20
+ - **Type Guards**: A collection of type guard functions to narrow down types safely.
21
+ - **JSON Handling**: Type-safe JSON parsing and stringification.
22
+ - **And more**: Including memoization, casting utilities, and other helpful tools.
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install ts-data-forge
28
+ ```
29
+
30
+ Or with other package managers:
31
+
32
+ ```bash
33
+ # Yarn
34
+ yarn add ts-data-forge
35
+
36
+ # pnpm
37
+ pnpm add ts-data-forge
38
+ ```
39
+
40
+ ## TypeScript Configuration
41
+
42
+ ts-data-forge works best with strict TypeScript settings:
43
+
44
+ ```json
45
+ {
46
+ "compilerOptions": {
47
+ "strict": true, // important
48
+ "noUncheckedIndexedAccess": true, // important
49
+ "noPropertyAccessFromIndexSignature": true, // important
50
+ "noFallthroughCasesInSwitch": true,
51
+ "noImplicitOverride": true,
52
+ "noImplicitReturns": true,
53
+ "noUnusedLocals": true,
54
+ "noUnusedParameters": true,
55
+ "allowUnreachableCode": false,
56
+ "allowUnusedLabels": false,
57
+ "exactOptionalPropertyTypes": false
58
+ }
59
+ }
60
+ ```
61
+
62
+ ## Core Modules
63
+
64
+ ### 🎯 [Functional Programming](./documents/functional.md)
65
+
66
+ Essential FP utilities for cleaner, more reliable code.
67
+
68
+ - **Optional** - Type-safe null handling
69
+ - **Result** - Error handling without exceptions
70
+ - **Pipe** - Function composition utilities
71
+ - **match** - Pattern matching for TypeScript
72
+
73
+ ### 🛡️ Type Guards
74
+
75
+ Runtime type checking with TypeScript integration.
76
+
77
+ - **Type Checks** - `isString`, `isNumber`, `isNonNullish`, etc.
78
+ - **Object Guards** - `isRecord`, `isNonNullObject`, `hasKey`
79
+ - **Utility Guards** - `isNonEmptyString`, `isPrimitive`
80
+
81
+ ### 🔢 Number Utilities
82
+
83
+ Branded number types and safe arithmetic operations.
84
+
85
+ - **Branded Types** - `Int`, `Uint`, `SafeInt`, `FiniteNumber`
86
+ - **Range Types** - `Int16`, `Uint32`, `PositiveInt`, etc.
87
+ - **Math Utils** - Type-safe arithmetic operations
88
+
89
+ ### 🔧 Array Operations
90
+
91
+ Type-safe array and tuple utilities with functional programming patterns.
92
+
93
+ - **Array Utils** - Comprehensive array manipulation functions
94
+ - **Tuple Utils** - Type-safe tuple operations with compile-time guarantees
95
+
96
+ ### 📦 [Collections](./documents/collections.md)
97
+
98
+ Immutable data structures for safer state management.
99
+
100
+ - **IMap** - Immutable Map implementation
101
+ - **ISet** - Immutable Set implementation
102
+
103
+ And mutable Queue/Stack implementation
104
+
105
+ - **Queue** - FIFO queue with O(1) operations
106
+ - **Stack** - LIFO stack implementation
107
+
108
+ ### 🔍 Other Utilities
109
+
110
+ Additional helpers for common programming tasks.
111
+
112
+ - **Type Casting** - `castMutable`, `castReadonly`
113
+ - **Utilities** - `memoizeFunction`, `mapNullable`, `unknownToString`
114
+ - **Conditionals** - `ifThen` for conditional operations
115
+
116
+ ## Usage Examples
117
+
118
+ Here are some examples of how to use utilities from `ts-data-forge`:
119
+
120
+ ### 1. Compile-Time Type Assertions with `expectType`
121
+
122
+ The `expectType` utility allows you to make assertions about types at compile time. This is useful for ensuring type correctness in complex type manipulations or when refactoring.
123
+
124
+ ```typescript
125
+ import { expectType } from 'ts-data-forge';
126
+
127
+ type User = { id: number; name: string };
128
+ type Admin = { id: number; name: string; role: 'admin' };
129
+
130
+ // Assert that Admin extends User
131
+ expectType<Admin, User>('<=');
132
+
133
+ // Assert that User does not extend Admin
134
+ expectType<User, Admin>('!<=');
135
+
136
+ // Assert exact type equality
137
+ expectType<{ x: number }, { x: number }>('=');
138
+
139
+ // The following would cause a compile-time error:
140
+ // expectType<User, Admin>("="); // Error: Type 'User' is not strictly equal to type 'Admin'.
141
+
142
+ expectType<User, any>('!='); // Error: Comparisons with `any` are also strictly checked.
143
+ ```
144
+
145
+ ### 2. Functional Programming with `Optional`, `Result`, `pipe`, and `match`
146
+
147
+ Handle nullable values and error-prone operations safely.
148
+
149
+ ```typescript
150
+ import { Optional, Result, pipe, match } from 'ts-data-forge';
151
+
152
+ // Optional for nullable values
153
+ const maybeValue = Optional.some(42);
154
+ const nothing = Optional.none;
155
+
156
+ const doubled = Optional.map(maybeValue, (x) => x * 2);
157
+ console.log(Optional.unwrapOr(doubled, 0)); // 84
158
+
159
+ // Result for error handling
160
+ const success = Result.ok(42);
161
+ const failure = Result.err('Something went wrong');
162
+
163
+ const mapped = Result.map(success, (x) => x * 2);
164
+ if (Result.isOk(mapped)) {
165
+ console.log(mapped.value); // 84
166
+ }
167
+
168
+ // Advanced pipe usage
169
+ const processNumber = (input: number) =>
170
+ pipe(input)
171
+ .map((x) => x * 2) // Regular transformation
172
+ .map((x) => x + 10) // Chain transformations
173
+ .map((x) => (x > 50 ? Optional.some(x) : Optional.none)) // Convert to Optional
174
+ .mapOptional((x) => x / 2).value; // Continue with Optional chain and get final Optional<number>
175
+
176
+ console.log(processNumber(30)); // Optional.some(35)
177
+ console.log(processNumber(10)); // Optional.none
178
+
179
+ // Pattern matching with match
180
+ type Status = 'loading' | 'success' | 'error';
181
+
182
+ const handleStatus = (status: Status, data?: string) =>
183
+ match(status, {
184
+ loading: 'Please wait...',
185
+ success: `Data: ${data ?? 'No data'}`,
186
+ error: 'An error occurred',
187
+ });
188
+
189
+ console.log(handleStatus('loading')); // 'Please wait...'
190
+ console.log(handleStatus('success', 'Hello')); // 'Data: Hello'
191
+ console.log(handleStatus('error')); // 'An error occurred'
192
+
193
+ // Pattern matching with Result
194
+ const processResult = (result: Result<number, string>) =>
195
+ Result.isOk(result) ? `Success: ${result.value}` : `Error: ${result.value}`;
196
+
197
+ console.log(processResult(Result.ok(42))); // 'Success: 42'
198
+ console.log(processResult(Result.err('Failed'))); // 'Error: Failed'
199
+ ```
200
+
201
+ ### 3. Number Utilities with `Num` and Branded Number Types
202
+
203
+ The `Num` object provides safe and convenient functions for numerical operations.
204
+
205
+ ```typescript
206
+ import { Num } from 'ts-data-forge';
207
+
208
+ // Basic conversions
209
+ const num = Num.from('123'); // 123
210
+ const invalid = Num.from('abc'); // NaN
211
+
212
+ // Range checking
213
+ const inRange = Num.isInRange(0, 10);
214
+ console.log(inRange(5)); // true
215
+ console.log(inRange(0)); // true (inclusive lower bound)
216
+ console.log(inRange(10)); // false (exclusive upper bound)
217
+
218
+ // Clamping values
219
+ const clamp = Num.clamp(0, 100);
220
+ console.log(clamp(150)); // 100
221
+ console.log(clamp(-10)); // 0
222
+
223
+ // Rounding utilities
224
+ const round2 = Num.round(2);
225
+ console.log(round2(3.14159)); // 3.14
226
+
227
+ console.log(Num.roundAt(3.14159, 3)); // 3.142
228
+ console.log(Num.roundToInt(3.7) satisfies Int); // 4
229
+
230
+ // Type guards
231
+ declare const value: number;
232
+ if (Num.isNonZero(value)) {
233
+ // value is guaranteed to be non-zero
234
+ const result = Num.div(10, value); // Safe division
235
+ }
236
+ ```
237
+
238
+ #### Branded Number Types for Enhanced Type Safety
239
+
240
+ `ts-data-forge` provides branded number types that enforce specific constraints at the type level.
241
+
242
+ ```typescript
243
+ import {
244
+ asInt,
245
+ asUint,
246
+ asFiniteNumber,
247
+ asSafeInt,
248
+ asInt16,
249
+ asUint32,
250
+ asNonZeroInt,
251
+ asPositiveInt,
252
+ Int,
253
+ Uint,
254
+ FiniteNumber,
255
+ SafeInt,
256
+ Int16,
257
+ Uint32,
258
+ NonZeroInt,
259
+ PositiveInt,
260
+ } from 'ts-data-forge';
261
+
262
+ // Basic branded types
263
+ const integer = asInt(42); // Int - any integer
264
+ const unsigned = asUint(42); // Uint - non-negative integer
265
+ const finite = asFiniteNumber(3.14); // FiniteNumber - finite floating-point
266
+ const safeInt = asSafeInt(42); // SafeInt - integer in safe range
267
+
268
+ const nonInteger = asInt(3.14); // This line causes a runtime error
269
+
270
+ // Range-constrained types (16-bit, 32-bit)
271
+ const int16 = asInt16(1000); // Int16: [-32768, 32767]
272
+ const uint32 = asUint32(3000000000); // Uint32: [0, 4294967295]
273
+
274
+ // Non-zero and positive variants
275
+ const nonZeroInt = asNonZeroInt(5); // NonZeroInt - excludes zero
276
+ const positiveInt = asPositiveInt(10); // PositiveInt - excludes zero and negatives
277
+
278
+ // Type-safe arithmetic with automatic clamping
279
+ const sum = Int16.add(int16, asInt16(2000)); // Int16 (3000)
280
+ const clamped = Int16.clamp(100000); // Int16 (32767 - clamped to MAX_VALUE)
281
+
282
+ // Safe division with non-zero types
283
+ const ratio = NonZeroInt.div(asNonZeroInt(10), nonZeroInt); // No division by zero risk
284
+
285
+ // Random generation within type constraints
286
+ const randomInt16 = Int16.random(); // Int16 (random value in valid range)
287
+ ```
288
+
289
+ ### 4. Array Utilities with `Arr`
290
+
291
+ The `Arr` object provides a rich set of functions for array manipulation.
292
+
293
+ ```typescript
294
+ import { Arr } from 'ts-data-forge';
295
+
296
+ const numbers: readonly number[] = [1, 2, 3, 4, 5, 2, 3];
297
+ const people = [
298
+ { name: 'Alice', age: 30 },
299
+ { name: 'Bob', age: 25 },
300
+ { name: 'Charlie', age: 35 },
301
+ ] as const;
302
+
303
+ // Reduction
304
+ const sum = Arr.sum(numbers);
305
+ console.log(sum); // 20
306
+
307
+ // Array creation
308
+ const zeros: readonly [0, 0, 0, 0, 0] = Arr.zeros(5); // [0, 0, 0, 0, 0]
309
+ const range: readonly [1, 2, 3] = Arr.range(1, 4); // [1, 2, 3]
310
+
311
+ // Type-safe length checking
312
+ if (Arr.isArrayAtLeastLength(numbers, 2)) {
313
+ // numbers is now guaranteed to have at least 3 elements
314
+ expectType<typeof numbers, readonly [number, number, ...number[]]>('=');
315
+ console.log(numbers[1]); // Safe access to index 2
316
+ }
317
+
318
+ // Take first n elements
319
+ const firstThree = Arr.take(numbers, 3); // [1, 2, 3]
320
+
321
+ // Find maximum by property
322
+ const oldestPerson = Arr.maxBy(people, (person) => person.age);
323
+ console.log(oldestPerson?.name); // 'Charlie'
324
+
325
+ // Remove duplicates
326
+ const unique = Arr.uniq(numbers); // [1, 2, 3, 4, 5]
327
+ ```
328
+
329
+ ### 5. Immutable Collections: `IMap` and `ISet`
330
+
331
+ Type-safe, immutable data structures.
332
+
333
+ ```typescript
334
+ import { IMap, ISet, pipe } from 'ts-data-forge';
335
+
336
+ // IMap usage - immutable operations
337
+ const originalMap = IMap.create<string, number>([]);
338
+ const mapWithOne = originalMap.set('one', 1);
339
+ const mapWithTwo = mapWithOne.set('two', 2);
340
+
341
+ // Original map is unchanged
342
+ console.log(originalMap.size); // 0
343
+ console.log(mapWithTwo.get('one')); // Optional.some(1)
344
+ console.log(mapWithTwo.has('three')); // false
345
+
346
+ // Using pipe for fluent updates
347
+ const idMap = pipe(Arr.seq(10))
348
+ .map(Arr.map<number>(i => [i, i.toString()])
349
+ .map(Arr.skip(1)) // [ [1, "1"], ..., [9, "9"]]
350
+ .map(IMap.create<number, string>).value;
351
+
352
+ console.log(idMap.size); // 9
353
+
354
+ // Efficient batch updates with withMutations
355
+ const idMapUpdated = idMap.withMutations([
356
+ { type: 'set', key: 99, value: "99" },
357
+ { type: 'update', key: 5, value: "five" },
358
+ { type: 'delete', key: 4 },
359
+ ]);
360
+
361
+ console.log(idMapUpdated.size); // 9
362
+
363
+ // ISet usage
364
+ const originalSet = ISet.create<number>([]);
365
+ const setWithItems = originalSet.add(1).add(2).add(1); // Duplicate ignored
366
+
367
+ console.log(originalSet.size); // 0 (unchanged)
368
+ console.log(setWithItems.has(1)); // true
369
+ console.log(setWithItems.size); // 2
370
+ ```
371
+
372
+ ### 6. Type Guards
373
+
374
+ Safe type narrowing with comprehensive type guards.
375
+
376
+ ```typescript
377
+ import { isNonNullObject, isRecord, hasKey } from 'ts-data-forge';
378
+
379
+ function processData(data: unknown) {
380
+ if (isRecord(data)) {
381
+ // data is now UnknownRecord (= Readonly<Record<string, unknown>>)
382
+ if (
383
+ hasKey(data, 'name') &&
384
+ // data is now ReadonlyRecord<"name", unknown> & UnknownRecord
385
+ typeof data.name === 'string'
386
+ ) {
387
+ console.log(`Hello, ${data.name}!`);
388
+ }
389
+ }
390
+ }
391
+
392
+ // Non-null object checking
393
+ declare const value: unknown;
394
+ if (isNonNullObject(value)) {
395
+ // value is guaranteed to be a non-null object
396
+ console.log(Object.keys(value));
397
+ }
398
+ ```
399
+
400
+ ### 7. Iteration with `range`
401
+
402
+ Generate ranges for iteration and array creation.
403
+
404
+ ```typescript
405
+ import { range } from 'ts-data-forge';
406
+
407
+ // Traditional for loop using range
408
+ for (const i of range(0, 5)) {
409
+ console.log(i); // 0, 1, 2, 3, 4
410
+ }
411
+
412
+ // Create arrays from ranges
413
+ const numbers = Array.from(range(1, 4)); // [1, 2, 3]
414
+ const squares = Array.from(range(1, 6), (x) => x * x); // [1, 4, 9, 16, 25]
415
+
416
+ // Step ranges
417
+ for (const i of range(0, 10, 2)) {
418
+ console.log(i); // 0, 2, 4, 6, 8
419
+ }
420
+ ```
421
+
422
+ ### 8. Mutability Utilities with `castMutable`
423
+
424
+ Safely work with readonly types when interfacing with mutable APIs.
425
+
426
+ ```tsx
427
+ import { Autocomplete } from '@mui/material';
428
+ import { castMutable } from 'ts-data-forge';
429
+
430
+ const readonlyOptions: readonly string[] = ['Option 1', 'Option 2', 'Option 3'];
431
+
432
+ const SomeComponent: React.FC = () => {
433
+ // Component implementation
434
+ return (
435
+ <Autocomplete
436
+ // Use castMutable to safely pass readonly array to mutable API. This is safer than casting with `as`, as it simply changes type `readonly T[]` to `T[]`.
437
+ options={castMutable(readonlyOptions)}
438
+ // ...
439
+ />
440
+ );
441
+ };
442
+
443
+ // Immer.js example - draft properties need mutable types
444
+ import { produce } from 'immer';
445
+
446
+ type State = Readonly<{
447
+ items: readonly string[];
448
+ }>;
449
+
450
+ const initialState: State = {
451
+ items: ['item1', 'item2'],
452
+ } as const;
453
+
454
+ const newItems: readonly string[] = ['newItem1', 'newItem2'];
455
+
456
+ const updatedState = produce(initialState, (draft) => {
457
+ // draft.items expects mutable array, but newItems is readonly
458
+ draft.items = castMutable(newItems); // Safe cast for assignment
459
+ });
460
+
461
+ console.log(updatedState.items); // ['newItem1', 'newItem2']
462
+ ```
463
+
464
+ ## Modules Overview
465
+
466
+ - **`expect-type`**: Compile-time type assertion utilities for testing and verification.
467
+ - **`guard`**: Type guard functions for safe type narrowing (e.g., `isNonNullObject`, `isRecord`).
468
+ - **`functional`**: Functional programming helpers like `Optional`, `Result`, `pipe`, and `match`.
469
+ - **`number`**: Comprehensive numerical utilities including the `Num` namespace and an extensive collection of branded number types (`Int`, `Uint`, `SafeInt`, `Int16`, `Int32`, `Uint16`, `Uint32`, `NonZeroInt`, `PositiveInt`, `NonNegativeFiniteNumber`, etc.) with type-safe arithmetic operations, range checking, and automatic clamping.
470
+ - **`array`**: Utilities for working with arrays and tuples, including creation, transformation, and type-safe operations.
471
+ - **`object`**: Utilities for working with records/objects (e.g., `Obj.shallowEq`).
472
+ - **`json`**: Type-safe JSON parsing and stringification utilities.
473
+ - **`collections`**: Immutable data structures like `IMap`, `ISet`, and `Queue` with full type safety.
474
+ - **`iterator`**: Utilities for working with iterators and generators (e.g., `range`).
475
+ - **`others`**: Miscellaneous utilities like `castMutable`, `castReadonly`, `ifThen`, `mapNullable`, `memoizeFunction`, `tuple`, `unknownToString`.
476
+
477
+ ## Key Benefits
478
+
479
+ - **Type Safety**: All utilities are designed with TypeScript's type system in mind, providing compile-time guarantees.
480
+ - **Immutability**: Data structures and operations promote immutable patterns for safer, more predictable code.
481
+ - **Functional Programming**: Support for functional programming paradigms with utilities like `Optional`, `Result`, and `pipe`.
482
+ - **Zero Runtime Dependencies**: The library has no external runtime dependencies, keeping your bundle size minimal.
483
+ - **Comprehensive Testing**: All utilities are thoroughly tested with both runtime and compile-time tests.
484
+
485
+ **Important Notes:**
486
+
487
+ - This library **only supports ESM (ES Modules)**. CommonJS is not supported.
488
+ - This library uses advanced TypeScript features, including branded types for enhanced type safety. Some functions require specific branded types as parameters (such as `Uint32` in `newArray`). The examples above use the small literal numeric values specifically allowed in each function for brevity, but in actual use you should use the provided type conversion functions (such as `asUint32`) or cast to the appropriate branded type, for example `as Uint32`.
489
+
490
+ ## Removing `expectType` in Production
491
+
492
+ Since `expectType` is only used for compile-time type checking, you should remove these calls in production builds for better performance.
493
+
494
+ ### Rollup Configuration
495
+
496
+ ```javascript
497
+ import rollupPluginStrip from '@rollup/plugin-strip';
498
+
499
+ export default {
500
+ // ... other config
501
+ plugins: [
502
+ // ... other plugins
503
+ rollupPluginStrip({
504
+ functions: ['expectType'],
505
+ include: '**/*.(mts|ts|mjs|js)',
506
+ }),
507
+ ],
508
+ };
509
+ ```
510
+
511
+ ### Vite Configuration
512
+
513
+ ```javascript
514
+ import { defineConfig } from 'vite';
515
+
516
+ export default defineConfig({
517
+ // ... other config
518
+ build: {
519
+ terserOptions: {
520
+ compress: {
521
+ pure_funcs: ['expectType'],
522
+ },
523
+ },
524
+ },
525
+ });
526
+ ```
527
+
528
+ ## Contributing
529
+
530
+ Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for detailed guidelines on how to contribute to this project.
531
+
532
+ ## License
533
+
534
+ This project is licensed under the [Apache License 2.0](./LICENSE).
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "ts-data-forge",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "keywords": [
6
+ "typescript",
7
+ "utility",
8
+ "types",
9
+ "static-typing",
10
+ "functional-programming",
11
+ "type-guard"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/noshiro-pf/ts-data-forge.git"
16
+ },
17
+ "license": "Apache-2.0",
18
+ "author": "noshiro-pf <noshiro.pf@gmail.com>",
19
+ "sideEffects": false,
20
+ "type": "module",
21
+ "exports": {
22
+ ".": {
23
+ "import": {
24
+ "types": "./dist/index.d.mts",
25
+ "default": "./dist/index.mjs"
26
+ }
27
+ }
28
+ },
29
+ "module": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "files": [
32
+ "src",
33
+ "dist",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "scripts": {
38
+ "build": "npm run z:node-eval -- \"import { build } from './scripts/functions/build.mjs'; build();\"",
39
+ "check-all": "npm run z:node-eval -- \"import { checkAll } from './scripts/cmd/check-all.mjs'; checkAll();\"",
40
+ "check:ext": "npm run z:node-eval -- \"import { checkExt } from './scripts/functions/check-ext.mjs'; checkExt();\"",
41
+ "cspell": "cspell \"**\" --gitignore --gitignore-root ./ --no-progress",
42
+ "doc": "npm run z:node-eval -- \"import { genDocs } from './scripts/functions/gen-docs.mjs'; genDocs();\"",
43
+ "fmt": "prettier --write .",
44
+ "gi": "npm run z:node-eval -- \"import { genIndex } from './scripts/functions/gen-index.mjs'; genIndex();\"",
45
+ "lint": "eslint .",
46
+ "lint:fix": "eslint . --fix",
47
+ "md": "markdownlint-cli2",
48
+ "test": "npm run z:vitest -- run",
49
+ "test:cov": "npm run z:vitest -- run --coverage",
50
+ "test:cov:ui": "vite preview --outDir ./coverage",
51
+ "test:ui": "npm run z:vitest -- --ui",
52
+ "testw": "npm run z:vitest -- watch",
53
+ "tsc": "tsc --noEmit",
54
+ "tscw": "tsc --noEmit --watch",
55
+ "type-check": "tsc --noEmit",
56
+ "update-packages": "npx npm-check-updates -u --install always --reject @types/node",
57
+ "z:node-eval": "node --import tsx/esm --input-type=module --eval",
58
+ "z:vitest": "vitest --config ./configs/vitest.config.ts"
59
+ },
60
+ "dependencies": {
61
+ "ts-type-forge": "^2.0.2"
62
+ },
63
+ "devDependencies": {
64
+ "@eslint/js": "^9.29.0",
65
+ "@rollup/plugin-replace": "^6.0.2",
66
+ "@rollup/plugin-strip": "^3.0.4",
67
+ "@rollup/plugin-typescript": "^12.1.2",
68
+ "@semantic-release/changelog": "^6.0.3",
69
+ "@semantic-release/commit-analyzer": "^13.0.1",
70
+ "@semantic-release/exec": "^7.1.0",
71
+ "@semantic-release/git": "^10.0.1",
72
+ "@semantic-release/github": "^11.0.3",
73
+ "@semantic-release/npm": "^12.0.1",
74
+ "@semantic-release/release-notes-generator": "^14.0.3",
75
+ "@types/node": "^20.19.0",
76
+ "@vitest/coverage-v8": "^3.2.3",
77
+ "@vitest/ui": "^3.2.3",
78
+ "conventional-changelog-conventionalcommits": "^9.0.0",
79
+ "cspell": "^9.1.1",
80
+ "eslint": "^9.29.0",
81
+ "fast-glob": "^3.3.3",
82
+ "markdownlint-cli2": "^0.18.1",
83
+ "prettier": "^3.5.3",
84
+ "prettier-plugin-organize-imports": "^4.1.0",
85
+ "prettier-plugin-packagejson": "^2.5.15",
86
+ "rollup": "^4.43.0",
87
+ "semantic-release": "^24.2.5",
88
+ "tsx": "^4.20.3",
89
+ "typedoc": "^0.28.5",
90
+ "typedoc-plugin-markdown": "^4.6.4",
91
+ "typescript": "^5.8.3",
92
+ "typescript-eslint": "^8.34.0",
93
+ "vitest": "^3.2.3"
94
+ },
95
+ "peerDependencies": {
96
+ "typescript": ">=4.8"
97
+ },
98
+ "engines": {
99
+ "node": ">=20.11.0"
100
+ }
101
+ }