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
package/README.md
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
# ts-data-forge
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/ts-data-forge)
|
|
4
|
+
[](https://www.npmjs.com/package/ts-data-forge)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](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
|
+
}
|