@satoshibits/functional 1.0.2
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/README.md +242 -0
- package/dist/array-utils.d.mts +317 -0
- package/dist/array-utils.d.mts.map +1 -0
- package/dist/array-utils.mjs +370 -0
- package/dist/array-utils.mjs.map +1 -0
- package/dist/composition.d.mts +603 -0
- package/dist/composition.d.mts.map +1 -0
- package/dist/composition.mjs +516 -0
- package/dist/composition.mjs.map +1 -0
- package/dist/object-utils.d.mts +267 -0
- package/dist/object-utils.d.mts.map +1 -0
- package/dist/object-utils.mjs +258 -0
- package/dist/object-utils.mjs.map +1 -0
- package/dist/option.d.mts +622 -0
- package/dist/option.d.mts.map +1 -0
- package/dist/option.mjs +637 -0
- package/dist/option.mjs.map +1 -0
- package/dist/performance.d.mts +265 -0
- package/dist/performance.d.mts.map +1 -0
- package/dist/performance.mjs +453 -0
- package/dist/performance.mjs.map +1 -0
- package/dist/pipeline.d.mts +431 -0
- package/dist/pipeline.d.mts.map +1 -0
- package/dist/pipeline.mjs +460 -0
- package/dist/pipeline.mjs.map +1 -0
- package/dist/predicates.d.mts +722 -0
- package/dist/predicates.d.mts.map +1 -0
- package/dist/predicates.mjs +802 -0
- package/dist/predicates.mjs.map +1 -0
- package/dist/reader-result.d.mts +422 -0
- package/dist/reader-result.d.mts.map +1 -0
- package/dist/reader-result.mjs +758 -0
- package/dist/reader-result.mjs.map +1 -0
- package/dist/result.d.mts +684 -0
- package/dist/result.d.mts.map +1 -0
- package/dist/result.mjs +814 -0
- package/dist/result.mjs.map +1 -0
- package/dist/types.d.mts +439 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +191 -0
- package/dist/types.mjs.map +1 -0
- package/dist/validation.d.mts +622 -0
- package/dist/validation.d.mts.map +1 -0
- package/dist/validation.mjs +852 -0
- package/dist/validation.mjs.map +1 -0
- package/package.json +46 -0
package/dist/option.mjs
ADDED
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module option
|
|
4
|
+
* @description Option/Maybe type for explicit null/undefined handling in a functional way.
|
|
5
|
+
* Provides a safe alternative to nullable values by wrapping them in a container type.
|
|
6
|
+
* Forces explicit handling of edge cases and eliminates null pointer exceptions.
|
|
7
|
+
* Inspired by functional programming languages like Haskell and Rust.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { Option, some, none, fromNullable, map, getOrElse } from './option.mts';
|
|
12
|
+
*
|
|
13
|
+
* // creating options
|
|
14
|
+
* const user = some({ id: '123', name: 'Alice' });
|
|
15
|
+
* const notFound = none();
|
|
16
|
+
* const maybeUser = fromNullable(localStorage.getItem('user'));
|
|
17
|
+
*
|
|
18
|
+
* // transforming values
|
|
19
|
+
* const userName = map((u: User) => u.name)(user);
|
|
20
|
+
*
|
|
21
|
+
* // extracting values safely
|
|
22
|
+
* const name = getOrElse(() => 'Anonymous')(userName);
|
|
23
|
+
*
|
|
24
|
+
* // chaining operations
|
|
25
|
+
* const greeting = pipe(
|
|
26
|
+
* fromNullable(getUserById(id)),
|
|
27
|
+
* map(u => u.name),
|
|
28
|
+
* map(name => `Hello, ${name}!`),
|
|
29
|
+
* getOrElse(() => 'Hello, stranger!')
|
|
30
|
+
* );
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @category Core
|
|
34
|
+
* @since 2025-07-03
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Creates a Some variant containing the provided value.
|
|
38
|
+
* @description Wraps a value in the Some variant of Option.
|
|
39
|
+
* Use this when you have a value that definitely exists.
|
|
40
|
+
*
|
|
41
|
+
* @template T - The type of the value to wrap
|
|
42
|
+
* @param {T} value - The value to wrap in Some
|
|
43
|
+
* @returns {Option<T>} A Some variant containing the value
|
|
44
|
+
*
|
|
45
|
+
* @category Constructors
|
|
46
|
+
* @example
|
|
47
|
+
* const user = some({ id: '123', name: 'Alice' });
|
|
48
|
+
* // => { _tag: 'Some', value: { id: '123', name: 'Alice' } }
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // Wrapping a found value
|
|
52
|
+
* const found = database.find(id);
|
|
53
|
+
* const result = found ? some(found) : none();
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // Creating from non-null assertion
|
|
57
|
+
* const config = getConfig();
|
|
58
|
+
* if (config.apiKey) {
|
|
59
|
+
* return some(config.apiKey);
|
|
60
|
+
* }
|
|
61
|
+
*
|
|
62
|
+
* @see none - Create an empty Option
|
|
63
|
+
* @see fromNullable - Create Option from nullable value
|
|
64
|
+
* @since 2025-07-03
|
|
65
|
+
*/
|
|
66
|
+
export var some = function (value) { return ({
|
|
67
|
+
_tag: 'Some',
|
|
68
|
+
value: value,
|
|
69
|
+
}); };
|
|
70
|
+
/**
|
|
71
|
+
* Creates a None variant representing no value.
|
|
72
|
+
* @description Creates the None variant of Option, representing absence of value.
|
|
73
|
+
* Use this when you want to explicitly represent "no value" in a type-safe way.
|
|
74
|
+
*
|
|
75
|
+
* @returns {Option<never>} A None variant
|
|
76
|
+
*
|
|
77
|
+
* @category Constructors
|
|
78
|
+
* @example
|
|
79
|
+
* const notFound = none();
|
|
80
|
+
* // => { _tag: 'None' }
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* // Representing search miss
|
|
84
|
+
* const user = users.find(u => u.id === targetId);
|
|
85
|
+
* return user ? some(user) : none();
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* // Empty configuration
|
|
89
|
+
* const apiKey = process.env.API_KEY;
|
|
90
|
+
* return apiKey ? some(apiKey) : none();
|
|
91
|
+
*
|
|
92
|
+
* @see some - Create an Option with a value
|
|
93
|
+
* @see fromNullable - Create Option from nullable value
|
|
94
|
+
* @since 2025-07-03
|
|
95
|
+
*/
|
|
96
|
+
export var none = function () { return ({
|
|
97
|
+
_tag: 'None',
|
|
98
|
+
}); };
|
|
99
|
+
/**
|
|
100
|
+
* Creates an Option from a nullable value.
|
|
101
|
+
* @description Converts a nullable value into an Option.
|
|
102
|
+
* Returns Some if the value is not null/undefined, None otherwise.
|
|
103
|
+
* This is the primary way to bridge nullable APIs with Option.
|
|
104
|
+
*
|
|
105
|
+
* @template T - The type of the non-null value
|
|
106
|
+
* @param {T | null | undefined} value - The nullable value to convert
|
|
107
|
+
* @returns {Option<T>} Some if value exists, None otherwise
|
|
108
|
+
*
|
|
109
|
+
* @category Constructors
|
|
110
|
+
* @example
|
|
111
|
+
* const maybeUser = fromNullable(localStorage.getItem('user'));
|
|
112
|
+
* // => Some(userData) if exists, None if null
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* // Safe property access
|
|
116
|
+
* const email = fromNullable(user?.contact?.email);
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* // Array element access
|
|
120
|
+
* const firstItem = fromNullable(items[0]);
|
|
121
|
+
*
|
|
122
|
+
* @see some - Wrap a non-null value
|
|
123
|
+
* @see none - Create an empty Option
|
|
124
|
+
* @since 2025-07-03
|
|
125
|
+
*/
|
|
126
|
+
export var fromNullable = function (value) {
|
|
127
|
+
return value === null || value === undefined ? none() : some(value);
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Creates an Option from a predicate function.
|
|
131
|
+
* Returns Some if the predicate is true, None otherwise.
|
|
132
|
+
*
|
|
133
|
+
* @category Constructors
|
|
134
|
+
* @example
|
|
135
|
+
* const positive = Option.fromPredicate((n: number) => n > 0);
|
|
136
|
+
* positive(5); // => Some(5)
|
|
137
|
+
* positive(-1); // => None
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* // Validation wrapper
|
|
141
|
+
* const validEmail = Option.fromPredicate((s: string) =>
|
|
142
|
+
* /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s)
|
|
143
|
+
* );
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* // Range check
|
|
147
|
+
* const inRange = Option.fromPredicate((n: number) => n >= 0 && n <= 100);
|
|
148
|
+
*/
|
|
149
|
+
export var fromPredicate = function (predicate) {
|
|
150
|
+
return function (value) {
|
|
151
|
+
return predicate(value) ? some(value) : none();
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Type guard to check if an Option is Some.
|
|
156
|
+
*
|
|
157
|
+
* @category Type Guards
|
|
158
|
+
* @example
|
|
159
|
+
* const opt = Option.fromNullable(getValue());
|
|
160
|
+
* if (Option.isSome(opt)) {
|
|
161
|
+
* console.log(opt.value); // TypeScript knows opt.value exists
|
|
162
|
+
* }
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* // Filtering array of options
|
|
166
|
+
* const values = options.filter(Option.isSome).map(opt => opt.value);
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* // Early return pattern
|
|
170
|
+
* if (!Option.isSome(result)) {
|
|
171
|
+
* return defaultValue;
|
|
172
|
+
* }
|
|
173
|
+
* return processValue(result.value);
|
|
174
|
+
*/
|
|
175
|
+
export var isSome = function (option) {
|
|
176
|
+
return option._tag === 'Some';
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Type guard to check if an Option is None.
|
|
180
|
+
*
|
|
181
|
+
* @category Type Guards
|
|
182
|
+
* @example
|
|
183
|
+
* const user = findUser(id);
|
|
184
|
+
* if (Option.isNone(user)) {
|
|
185
|
+
* throw new Error('User not found');
|
|
186
|
+
* }
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* // Conditional rendering
|
|
190
|
+
* if (Option.isNone(data)) {
|
|
191
|
+
* return <LoadingSpinner />;
|
|
192
|
+
* }
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* // Validation check
|
|
196
|
+
* const validated = validate(input);
|
|
197
|
+
* if (Option.isNone(validated)) {
|
|
198
|
+
* return { error: 'Invalid input' };
|
|
199
|
+
* }
|
|
200
|
+
*/
|
|
201
|
+
export var isNone = function (option) {
|
|
202
|
+
return option._tag === 'None';
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Maps a function over the value in Some, does nothing for None.
|
|
206
|
+
*
|
|
207
|
+
* @category Transformations
|
|
208
|
+
* @example
|
|
209
|
+
* const doubled = Option.map((n: number) => n * 2);
|
|
210
|
+
* doubled(Option.some(5)); // => Some(10)
|
|
211
|
+
* doubled(Option.none()); // => None
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* // Transform user data
|
|
215
|
+
* const userName = pipe(
|
|
216
|
+
* findUser(id),
|
|
217
|
+
* Option.map(user => user.name),
|
|
218
|
+
* Option.map(name => name.toUpperCase())
|
|
219
|
+
* );
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* // Parse and transform
|
|
223
|
+
* const parsed = pipe(
|
|
224
|
+
* Option.fromNullable(jsonString),
|
|
225
|
+
* Option.map(str => JSON.parse(str)),
|
|
226
|
+
* Option.map(data => data.value)
|
|
227
|
+
* );
|
|
228
|
+
*/
|
|
229
|
+
export var map = function (fn) {
|
|
230
|
+
return function (option) {
|
|
231
|
+
return isSome(option) ? some(fn(option.value)) : none();
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* FlatMaps a function over the value in Some, does nothing for None.
|
|
236
|
+
* Also known as chain or bind in other libraries.
|
|
237
|
+
*
|
|
238
|
+
* @category Transformations
|
|
239
|
+
* @example
|
|
240
|
+
* const safeDivide = (n: number) =>
|
|
241
|
+
* n === 0 ? Option.none() : Option.some(10 / n);
|
|
242
|
+
*
|
|
243
|
+
* const result = pipe(
|
|
244
|
+
* Option.some(5),
|
|
245
|
+
* Option.flatMap(safeDivide)
|
|
246
|
+
* ); // => Some(2)
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* // Chaining optional operations
|
|
250
|
+
* const getManager = (employee: Employee) =>
|
|
251
|
+
* Option.fromNullable(employee.managerId)
|
|
252
|
+
* .pipe(Option.flatMap(id => findEmployee(id)));
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* // Validation chain
|
|
256
|
+
* const processUser = pipe(
|
|
257
|
+
* parseUser(input),
|
|
258
|
+
* Option.flatMap(validateAge),
|
|
259
|
+
* Option.flatMap(validateEmail)
|
|
260
|
+
* );
|
|
261
|
+
*/
|
|
262
|
+
export var flatMap = function (fn) {
|
|
263
|
+
return function (option) {
|
|
264
|
+
return isSome(option) ? fn(option.value) : none();
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
/**
|
|
268
|
+
* Alias for flatMap - monadic bind operation.
|
|
269
|
+
*
|
|
270
|
+
* @category Transformations
|
|
271
|
+
* @see flatMap
|
|
272
|
+
*/
|
|
273
|
+
export var chain = flatMap;
|
|
274
|
+
/**
|
|
275
|
+
* Returns the value if Some, otherwise returns the provided default.
|
|
276
|
+
*
|
|
277
|
+
* @category Extractors
|
|
278
|
+
* @example
|
|
279
|
+
* const value = Option.getOrElse(() => 'default')(maybeValue);
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* // Configuration with defaults
|
|
283
|
+
* const port = pipe(
|
|
284
|
+
* Option.fromNullable(process.env.PORT),
|
|
285
|
+
* Option.map(parseInt),
|
|
286
|
+
* Option.getOrElse(() => 3000)
|
|
287
|
+
* );
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* // User preferences
|
|
291
|
+
* const theme = pipe(
|
|
292
|
+
* getUserPreference('theme'),
|
|
293
|
+
* Option.getOrElse(() => 'light')
|
|
294
|
+
* );
|
|
295
|
+
*/
|
|
296
|
+
export var getOrElse = function (defaultValue) {
|
|
297
|
+
return function (option) {
|
|
298
|
+
return isSome(option) ? option.value : defaultValue();
|
|
299
|
+
};
|
|
300
|
+
};
|
|
301
|
+
/**
|
|
302
|
+
* Returns the first Some option, or None if both are None.
|
|
303
|
+
* Useful for fallback chains.
|
|
304
|
+
*
|
|
305
|
+
* @category Combinations
|
|
306
|
+
* @example
|
|
307
|
+
* const config = Option.orElse(
|
|
308
|
+
* () => Option.fromNullable(process.env.API_KEY)
|
|
309
|
+
* )(Option.fromNullable(config.apiKey));
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* // Multiple fallbacks
|
|
313
|
+
* const findUser = (id: string) => pipe(
|
|
314
|
+
* findInCache(id),
|
|
315
|
+
* Option.orElse(() => findInDatabase(id)),
|
|
316
|
+
* Option.orElse(() => findInArchive(id))
|
|
317
|
+
* );
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* // Try alternative parsing
|
|
321
|
+
* const parsed = pipe(
|
|
322
|
+
* tryParseJSON(input),
|
|
323
|
+
* Option.orElse(() => tryParseYAML(input))
|
|
324
|
+
* );
|
|
325
|
+
*/
|
|
326
|
+
export var orElse = function (alternative) {
|
|
327
|
+
return function (option) {
|
|
328
|
+
return isSome(option) ? option : alternative();
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
export function filter(predicate) {
|
|
332
|
+
return function (option) {
|
|
333
|
+
return isSome(option) && predicate(option.value) ? option : none();
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Pattern matching for Option types.
|
|
338
|
+
* Provides exhaustive handling of both Some and None cases.
|
|
339
|
+
*
|
|
340
|
+
* @category Pattern Matching
|
|
341
|
+
* @example
|
|
342
|
+
* const message = Option.match({
|
|
343
|
+
* some: (user) => `Hello, ${user.name}!`,
|
|
344
|
+
* none: () => 'Hello, guest!'
|
|
345
|
+
* })(maybeUser);
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* // React component rendering
|
|
349
|
+
* const UserProfile = ({ userId }: Props) => {
|
|
350
|
+
* const user = useUser(userId);
|
|
351
|
+
*
|
|
352
|
+
* return Option.match({
|
|
353
|
+
* some: (u) => <Profile user={u} />,
|
|
354
|
+
* none: () => <NotFound />
|
|
355
|
+
* })(user);
|
|
356
|
+
* };
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* // API response handling
|
|
360
|
+
* const response = await Option.match({
|
|
361
|
+
* some: async (data) => api.update(data),
|
|
362
|
+
* none: async () => api.create(defaults)
|
|
363
|
+
* })(existingData);
|
|
364
|
+
*/
|
|
365
|
+
export var match = function (patterns) {
|
|
366
|
+
return function (option) {
|
|
367
|
+
return isSome(option) ? patterns.some(option.value) : patterns.none();
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* Converts an Option to a nullable value.
|
|
372
|
+
* Some(value) becomes value, None becomes null.
|
|
373
|
+
*
|
|
374
|
+
* @category Conversions
|
|
375
|
+
* @example
|
|
376
|
+
* const value = Option.toNullable(maybeValue);
|
|
377
|
+
* localStorage.setItem('key', value ?? '');
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* // Database update
|
|
381
|
+
* const update = {
|
|
382
|
+
* name: Option.toNullable(maybeName),
|
|
383
|
+
* email: Option.toNullable(maybeEmail),
|
|
384
|
+
* phone: Option.toNullable(maybePhone)
|
|
385
|
+
* };
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* // JSON serialization
|
|
389
|
+
* const data = {
|
|
390
|
+
* id: user.id,
|
|
391
|
+
* nickname: Option.toNullable(user.nickname)
|
|
392
|
+
* };
|
|
393
|
+
*/
|
|
394
|
+
export var toNullable = function (option) {
|
|
395
|
+
return isSome(option) ? option.value : null;
|
|
396
|
+
};
|
|
397
|
+
/**
|
|
398
|
+
* Converts an Option to undefined if None.
|
|
399
|
+
* Some(value) becomes value, None becomes undefined.
|
|
400
|
+
*
|
|
401
|
+
* @category Conversions
|
|
402
|
+
* @example
|
|
403
|
+
* const params = {
|
|
404
|
+
* limit: 10,
|
|
405
|
+
* offset: Option.toUndefined(maybeOffset)
|
|
406
|
+
* };
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
* // Optional chaining alternative
|
|
410
|
+
* const city = Option.toUndefined(
|
|
411
|
+
* Option.map((addr: Address) => addr.city)(maybeAddress)
|
|
412
|
+
* );
|
|
413
|
+
*/
|
|
414
|
+
export var toUndefined = function (option) {
|
|
415
|
+
return isSome(option) ? option.value : undefined;
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* Creates an Option from a function that might throw an error.
|
|
419
|
+
* Returns Some with the result if the function succeeds, None if it throws.
|
|
420
|
+
*
|
|
421
|
+
* @category Constructors
|
|
422
|
+
* @example
|
|
423
|
+
* const safeParse = (json: string) => Option.tryCatch(() => JSON.parse(json));
|
|
424
|
+
* safeParse('{"a":1}'); // => Some({ a: 1 })
|
|
425
|
+
* safeParse('invalid json'); // => None
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* // Safe file parsing
|
|
429
|
+
* const config = Option.tryCatch(() =>
|
|
430
|
+
* JSON.parse(fs.readFileSync('config.json', 'utf8'))
|
|
431
|
+
* );
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* // Safe URL parsing
|
|
435
|
+
* const url = Option.tryCatch(() => new URL(input));
|
|
436
|
+
*/
|
|
437
|
+
export var tryCatch = function (fn) {
|
|
438
|
+
try {
|
|
439
|
+
return some(fn());
|
|
440
|
+
}
|
|
441
|
+
catch (_a) {
|
|
442
|
+
return none();
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
/**
|
|
446
|
+
* Executes a side-effecting function on the value in Some.
|
|
447
|
+
* Returns the original Option unchanged.
|
|
448
|
+
*
|
|
449
|
+
* @category Side Effects
|
|
450
|
+
* @example
|
|
451
|
+
* const logValue = pipe(
|
|
452
|
+
* Option.some(42),
|
|
453
|
+
* Option.tap(value => console.log('Found value:', value))
|
|
454
|
+
* ); // logs "Found value: 42" and returns Some(42)
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* // Debug logging in a chain
|
|
458
|
+
* const result = pipe(
|
|
459
|
+
* getUserInput(),
|
|
460
|
+
* Option.tap(input => console.log('Raw input:', input)),
|
|
461
|
+
* Option.map(normalize),
|
|
462
|
+
* Option.tap(normalized => console.log('Normalized:', normalized)),
|
|
463
|
+
* Option.filter(isValid)
|
|
464
|
+
* );
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* // Side effects like analytics
|
|
468
|
+
* const trackEvent = pipe(
|
|
469
|
+
* findUser(id),
|
|
470
|
+
* Option.tap(user => analytics.track('user.found', { id: user.id }))
|
|
471
|
+
* );
|
|
472
|
+
*/
|
|
473
|
+
export var tap = function (fn) {
|
|
474
|
+
return function (option) {
|
|
475
|
+
if (isSome(option)) {
|
|
476
|
+
fn(option.value);
|
|
477
|
+
}
|
|
478
|
+
return option;
|
|
479
|
+
};
|
|
480
|
+
};
|
|
481
|
+
/**
|
|
482
|
+
* Namespace containing all Option utilities.
|
|
483
|
+
*
|
|
484
|
+
* @category Namespace
|
|
485
|
+
*/
|
|
486
|
+
export var Option = {
|
|
487
|
+
some: some,
|
|
488
|
+
none: none,
|
|
489
|
+
fromNullable: fromNullable,
|
|
490
|
+
fromPredicate: fromPredicate,
|
|
491
|
+
tryCatch: tryCatch,
|
|
492
|
+
isSome: isSome,
|
|
493
|
+
isNone: isNone,
|
|
494
|
+
map: map,
|
|
495
|
+
flatMap: flatMap,
|
|
496
|
+
chain: chain,
|
|
497
|
+
getOrElse: getOrElse,
|
|
498
|
+
orElse: orElse,
|
|
499
|
+
filter: filter,
|
|
500
|
+
tap: tap,
|
|
501
|
+
match: match,
|
|
502
|
+
toNullable: toNullable,
|
|
503
|
+
toUndefined: toUndefined,
|
|
504
|
+
};
|
|
505
|
+
/**
|
|
506
|
+
* Combines two Options using a binary function.
|
|
507
|
+
* Returns None if either Option is None.
|
|
508
|
+
*
|
|
509
|
+
* @category Combinations
|
|
510
|
+
* @example
|
|
511
|
+
* const add = (a: number, b: number) => a + b;
|
|
512
|
+
* const sum = lift2(add)(Option.some(5), Option.some(3));
|
|
513
|
+
* // => Some(8)
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
* // Form validation
|
|
517
|
+
* const createUser = (name: string, email: string) => ({ name, email });
|
|
518
|
+
* const validUser = lift2(createUser)(
|
|
519
|
+
* validateName(input.name),
|
|
520
|
+
* validateEmail(input.email)
|
|
521
|
+
* );
|
|
522
|
+
*
|
|
523
|
+
* @example
|
|
524
|
+
* // Coordinate operations
|
|
525
|
+
* const distance = (x: number, y: number) => Math.sqrt(x * x + y * y);
|
|
526
|
+
* const result = lift2(distance)(parseX(input), parseY(input));
|
|
527
|
+
*/
|
|
528
|
+
export var lift2 = function (fn) {
|
|
529
|
+
return function (optionA, optionB) {
|
|
530
|
+
return isSome(optionA) && isSome(optionB)
|
|
531
|
+
? some(fn(optionA.value, optionB.value))
|
|
532
|
+
: none();
|
|
533
|
+
};
|
|
534
|
+
};
|
|
535
|
+
/**
|
|
536
|
+
* Sequences an array of Options into an Option of array.
|
|
537
|
+
* Returns Some with all values if all are Some, None if any is None.
|
|
538
|
+
*
|
|
539
|
+
* @category Combinations
|
|
540
|
+
* @example
|
|
541
|
+
* const results = sequence([
|
|
542
|
+
* Option.some(1),
|
|
543
|
+
* Option.some(2),
|
|
544
|
+
* Option.some(3)
|
|
545
|
+
* ]);
|
|
546
|
+
* // => Some([1, 2, 3])
|
|
547
|
+
*
|
|
548
|
+
* @example
|
|
549
|
+
* // Parse multiple values
|
|
550
|
+
* const numbers = sequence(
|
|
551
|
+
* inputs.map(input => parseNumber(input))
|
|
552
|
+
* );
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* // Validate all fields
|
|
556
|
+
* const validatedFields = sequence([
|
|
557
|
+
* validateField('name', data.name),
|
|
558
|
+
* validateField('email', data.email),
|
|
559
|
+
* validateField('age', data.age)
|
|
560
|
+
* ]);
|
|
561
|
+
*/
|
|
562
|
+
export var sequence = function (options) {
|
|
563
|
+
var results = [];
|
|
564
|
+
for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
|
|
565
|
+
var option = options_1[_i];
|
|
566
|
+
if (isNone(option)) {
|
|
567
|
+
return none();
|
|
568
|
+
}
|
|
569
|
+
results.push(option.value);
|
|
570
|
+
}
|
|
571
|
+
return some(results);
|
|
572
|
+
};
|
|
573
|
+
/**
|
|
574
|
+
* Applies a function wrapped in an Option to a value wrapped in an Option.
|
|
575
|
+
*
|
|
576
|
+
* @category Apply
|
|
577
|
+
* @example
|
|
578
|
+
* const addOne = (n: number) => n + 1;
|
|
579
|
+
* const result = ap(Option.some(addOne))(Option.some(5));
|
|
580
|
+
* // => Some(6)
|
|
581
|
+
*
|
|
582
|
+
* @example
|
|
583
|
+
* // Partial application with options
|
|
584
|
+
* const add = (a: number) => (b: number) => a + b;
|
|
585
|
+
* const maybeAdd5 = Option.map(add)(Option.some(5));
|
|
586
|
+
* const result = ap(maybeAdd5)(Option.some(3));
|
|
587
|
+
* // => Some(8)
|
|
588
|
+
*/
|
|
589
|
+
export var ap = function (optionFn) {
|
|
590
|
+
return function (optionA) {
|
|
591
|
+
return isSome(optionFn) && isSome(optionA)
|
|
592
|
+
? some(optionFn.value(optionA.value))
|
|
593
|
+
: none();
|
|
594
|
+
};
|
|
595
|
+
};
|
|
596
|
+
/**
|
|
597
|
+
* Sequences a struct of Options into an Option of a struct.
|
|
598
|
+
* Returns Some with the struct of all values if all are Some, None if any is None.
|
|
599
|
+
*
|
|
600
|
+
* @category Combinations
|
|
601
|
+
* @example
|
|
602
|
+
* const result = sequenceS({
|
|
603
|
+
* a: Option.some(1),
|
|
604
|
+
* b: Option.some('hello')
|
|
605
|
+
* });
|
|
606
|
+
* // => Some({ a: 1, b: 'hello' })
|
|
607
|
+
*
|
|
608
|
+
* @example
|
|
609
|
+
* // Form validation
|
|
610
|
+
* const validForm = sequenceS({
|
|
611
|
+
* name: validateName(input.name),
|
|
612
|
+
* email: validateEmail(input.email),
|
|
613
|
+
* age: validateAge(input.age)
|
|
614
|
+
* });
|
|
615
|
+
*
|
|
616
|
+
* @example
|
|
617
|
+
* // Configuration validation
|
|
618
|
+
* const config = sequenceS({
|
|
619
|
+
* apiKey: Option.fromNullable(process.env.API_KEY),
|
|
620
|
+
* port: Option.tryCatch(() => parseInt(process.env.PORT!)),
|
|
621
|
+
* debug: Option.fromNullable(process.env.DEBUG).pipe(Option.map(v => v === 'true'))
|
|
622
|
+
* });
|
|
623
|
+
*/
|
|
624
|
+
export var sequenceS = function (struct) {
|
|
625
|
+
var result = {};
|
|
626
|
+
for (var key in struct) {
|
|
627
|
+
var option = struct[key];
|
|
628
|
+
if (option && isSome(option)) {
|
|
629
|
+
result[key] = option.value;
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
return none();
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return some(result);
|
|
636
|
+
};
|
|
637
|
+
//# sourceMappingURL=option.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"option.mjs","sourceRoot":"","sources":["../src/option.mts"],"names":[],"mappings":"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"}
|