functype 0.60.3 → 0.60.4

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.
@@ -0,0 +1,462 @@
1
+ # Functype Quick Reference
2
+
3
+ ## Option<T>
4
+
5
+ ```typescript
6
+ import { Option, Some, None } from "functype/option"
7
+
8
+ // Creation
9
+ Option(42) // Some(42)
10
+ Option(null) // None
11
+ Option(undefined) // None
12
+ Some(42) // Some(42)
13
+ None() // None
14
+ Option.from(42) // Some(42)
15
+ Option.none() // None
16
+
17
+ // Checking
18
+ option.isDefined() // true if Some, false if None
19
+ option.isEmpty() // true if None, false if Some
20
+
21
+ // Accessing
22
+ option.get() // Gets value or throws if None
23
+ option.orElse(defaultValue) // Gets value or returns default
24
+ option.orThrow(new Error("No value")) // Gets value or throws custom error
25
+
26
+ // Transforming
27
+ option.map((x) => x * 2) // Transforms inner value
28
+ option.flatMap((x) => Option(x.toString())) // Chains with operations returning Option
29
+ option.filter((x) => x > 10) // None if predicate fails, unchanged if passes
30
+
31
+ // Pattern matching
32
+ option.fold(
33
+ () => "empty", // Called for None
34
+ (value) => `value: ${value}`, // Called for Some
35
+ )
36
+
37
+ option.match({
38
+ Some: (value) => `value: ${value}`,
39
+ None: () => "empty",
40
+ })
41
+ ```
42
+
43
+ ## Either<L, R>
44
+
45
+ ```typescript
46
+ import { Either, Left, Right } from "functype/either"
47
+
48
+ // Creation
49
+ Right<string, number>(42) // Right<number>(42)
50
+ Left<string, number>("error") // Left<string>("error")
51
+ Either.right(42) // Right(42)
52
+ Either.left("error") // Left("error")
53
+ Either.fromNullable(value, "was null") // Left if null/undefined
54
+
55
+ // Checking
56
+ either.isRight() // true if Right, false if Left
57
+ either.isLeft() // true if Left, false if Right
58
+
59
+ // Accessing
60
+ either.get() // Gets Right value or throws if Left
61
+ either.orElse(defaultValue) // Gets Right value or returns default
62
+ either.getLeft() // Gets Left value or throws if Right
63
+
64
+ // Transforming
65
+ either.map((x) => x * 2) // Transforms Right value
66
+ either.mapLeft((e) => `Error: ${e}`) // Transforms Left value
67
+ either.flatMap((x) => Right(x.toString())) // Chains with operations returning Either
68
+ either.filter(
69
+ (x) => x > 10,
70
+ (x) => `${x} is too small`,
71
+ ) // Left if predicate fails
72
+
73
+ // Operations
74
+ either.swap() // Converts Left to Right and vice versa
75
+
76
+ // Pattern matching
77
+ either.fold(
78
+ (left) => `Error: ${left}`,
79
+ (right) => `Success: ${right}`,
80
+ )
81
+
82
+ either.match({
83
+ Right: (value) => `Success: ${value}`,
84
+ Left: (error) => `Error: ${error}`,
85
+ })
86
+ ```
87
+
88
+ ## Try<T>
89
+
90
+ ```typescript
91
+ import { Try, Success, Failure } from "functype/try"
92
+
93
+ // Creation
94
+ Try(() => 42) // Success(42)
95
+ Try(() => {
96
+ throw new Error("Failed")
97
+ }) // Failure(Error)
98
+ Success(42) // Success(42)
99
+ Failure(new Error("Failed")) // Failure(Error)
100
+
101
+ // Checking
102
+ tryVal.isSuccess() // true if Success, false if Failure
103
+ tryVal.isFailure() // true if Failure, false if Success
104
+
105
+ // Accessing
106
+ tryVal.get() // Gets value or throws original error
107
+ tryVal.orElse(defaultValue) // Gets value or returns default
108
+ tryVal.error // Gets error for Failure
109
+
110
+ // Error handling
111
+ tryVal.recover(defaultValue) // Success with original value or default
112
+ tryVal.recoverWith(() => Try(() => backup())) // Tries alternative computation on failure
113
+
114
+ // Transforming
115
+ tryVal.map((x) => x * 2) // Maps success value
116
+ tryVal.flatMap((x) => Try(() => operation(x))) // Chains with operations returning Try
117
+
118
+ // Conversions
119
+ tryVal.toOption() // Option.Some for Success, Option.None for Failure
120
+ tryVal.toEither() // Either.Right for Success, Either.Left for Failure
121
+
122
+ // Pattern matching
123
+ tryVal.fold(
124
+ (error) => `Error: ${error.message}`,
125
+ (value) => `Success: ${value}`,
126
+ )
127
+
128
+ tryVal.match({
129
+ Success: (value) => `Success: ${value}`,
130
+ Failure: (error) => `Error: ${error.message}`,
131
+ })
132
+ ```
133
+
134
+ ## List<T>
135
+
136
+ ```typescript
137
+ import { List } from "functype/list"
138
+
139
+ // Creation
140
+ List([1, 2, 3]) // List([1, 2, 3])
141
+ List.empty<number>() // List([])
142
+
143
+ // Properties
144
+ list.isEmpty() // true if empty, false otherwise
145
+ list.size() // Number of elements
146
+
147
+ // Accessing
148
+ list.head() // Option.Some with first element or None if empty
149
+ list.tail() // List with all elements except the first
150
+ list.at(2) // Option.Some with element at index or None
151
+
152
+ // Adding/removing
153
+ list.add(4) // New list with element added
154
+ list.addAll([4, 5, 6]) // New list with elements added
155
+ list.remove(2) // New list with element removed
156
+ list.removeAt(1) // New list with element at index removed
157
+
158
+ // Transforming
159
+ list.map((x) => x * 2) // List with transformed elements
160
+ list.flatMap((x) => List([x, x * 2])) // Transform + flatten
161
+ list.filter((x) => x % 2 === 0) // List with elements matching predicate
162
+
163
+ // Slicing
164
+ list.take(2) // First n elements
165
+ list.drop(2) // All elements after first n
166
+ list.slice(1, 3) // Elements from start to end (exclusive)
167
+
168
+ // Finding
169
+ list.find((x) => x > 10) // Option.Some with first match or None
170
+ list.exists((x) => x > 10) // true if any element matches predicate
171
+ list.forAll((x) => x > 0) // true if all elements match predicate
172
+ list.count((x) => x % 2 === 0) // Count of elements matching predicate
173
+
174
+ // Aggregating
175
+ list.foldLeft(0)((acc, x) => acc + x) // Reduce from left to right
176
+ list.foldRight(0)((x, acc) => x + acc) // Reduce from right to left
177
+ list.reduce((a, b) => a + b) // Reduce (without initial value)
178
+
179
+ // Operations
180
+ list.reverse() // Reversed list
181
+ list.sort((a, b) => a - b) // Sorted list
182
+ list.distinct() // List with duplicates removed
183
+ list.concat(List([4, 5, 6])) // Lists combined
184
+ list.groupBy((x) => (x % 2 === 0 ? "even" : "odd")) // Map of grouped elements
185
+ ```
186
+
187
+ ## Map<K, V>
188
+
189
+ ```typescript
190
+ import { Map } from "functype/map"
191
+
192
+ // Creation
193
+ Map({ a: 1, b: 2, c: 3 }) // Map({a: 1, b: 2, c: 3})
194
+ Map.empty<string, number>() // Empty map
195
+
196
+ // Properties
197
+ map.isEmpty() // true if empty
198
+ map.size() // Number of entries
199
+ map.has("a") // true if key exists
200
+
201
+ // Accessing
202
+ map.get("a") // Option.Some with value or None
203
+ map.orElse("a", 0) // Value or default
204
+ map.keys() // List of keys
205
+ map.values() // List of values
206
+ map.entries() // List of [key, value] tuples
207
+
208
+ // Modifying
209
+ map.add("d", 4) // New map with entry added
210
+ map.addAll({ d: 4, e: 5 }) // New map with entries added
211
+ map.remove("a") // New map with key removed
212
+ map.removeAll(["a", "b"]) // New map with keys removed
213
+
214
+ // Transforming
215
+ map.map((v) => v * 2) // Map with transformed values
216
+ map.mapEntries(([k, v]) => [`key_${k}`, v * 2]) // Transform both keys and values
217
+ map.filter((v) => v > 1) // Map with entries matching predicate
218
+ map.filterKeys((k) => k !== "a") // Map with entries having keys matching predicate
219
+
220
+ // Operations
221
+ map.merge(Map({ c: 30, d: 40 })) // Maps combined (right values override)
222
+ map.mergeWith(Map({ c: 30, d: 40 }), (v1, v2) => v1 + v2) // Maps combined with custom function
223
+
224
+ // Conversions
225
+ map.toObject() // Standard JS object
226
+ ```
227
+
228
+ ## Set<T>
229
+
230
+ ```typescript
231
+ import { Set } from "functype/set"
232
+
233
+ // Creation
234
+ Set([1, 2, 3]) // Set([1, 2, 3])
235
+ Set.empty<number>() // Empty set
236
+
237
+ // Properties
238
+ set.isEmpty() // true if empty
239
+ set.size() // Number of elements
240
+ set.has(2) // true if element exists
241
+
242
+ // Modifying
243
+ set.add(4) // New set with element added
244
+ set.addAll([4, 5]) // New set with elements added
245
+ set.remove(2) // New set with element removed
246
+ set.removeAll([1, 2]) // New set with elements removed
247
+
248
+ // Transforming
249
+ set.map((x) => x * 2) // Set with transformed elements
250
+ set.flatMap((x) => Set([x, x + 1])) // Transform + flatten
251
+ set.filter((x) => x % 2 === 0) // Set with elements matching predicate
252
+
253
+ // Set operations
254
+ set.union(Set([3, 4, 5])) // Union of sets
255
+ set.intersect(Set([2, 3, 4])) // Intersection of sets
256
+ set.difference(Set([3, 4])) // Elements in this set but not in other
257
+ set.symmetricDifference(Set([3, 4])) // Elements in either set but not both
258
+ set.isSubsetOf(Set([1, 2, 3, 4])) // true if all elements in other set
259
+ ```
260
+
261
+ ## Task
262
+
263
+ ```typescript
264
+ import { Task } from "functype/core/task"
265
+
266
+ // Synchronous tasks
267
+ const syncTask = Task().Sync(
268
+ () => 42,
269
+ (err) => new Error(`Failed: ${err}`),
270
+ )
271
+
272
+ // Asynchronous tasks
273
+ const asyncTask = Task().Async(
274
+ async () => await fetch("/api").then((r) => r.json()),
275
+ async (err) => new Error(`Fetch failed: ${err}`),
276
+ )
277
+
278
+ // Named tasks (for debugging and error identification)
279
+ const namedTask = Task({ name: "UserFetch" }).Sync(
280
+ () => ({ id: 1, name: "John" }),
281
+ (err) => new Error(`User fetch failed: ${err}`),
282
+ )
283
+
284
+ // Error handling with named tasks
285
+ try {
286
+ await Task({ name: "DataProcessor" }).Async(() => {
287
+ throw new Error("Processing failed")
288
+ })
289
+ } catch (error) {
290
+ console.log(error.name) // "DataProcessor"
291
+ console.log(error.taskInfo.name) // "DataProcessor"
292
+ }
293
+
294
+ // Companion functions
295
+ Task.success(42, { name: "SuccessResult" }) // Creates TaskResult
296
+ Task.fail(new Error("Failed"), data, { name: "FailureResult" }) // Creates TaskException with name
297
+
298
+ // Adapting functions
299
+ const fetchAPI = (id: string): Promise<User> => fetch(`/api/users/${id}`).then((r) => r.json())
300
+ const getUser = Task.fromPromise(fetchAPI, { name: "UserFetch" })
301
+
302
+ // Converting
303
+ const promise = Task.toPromise(syncTask) // Standard Promise
304
+ ```
305
+
306
+ ## Tuple
307
+
308
+ ```typescript
309
+ import { Tuple } from "functype/tuple"
310
+
311
+ // Creation
312
+ const pair = Tuple(42, "hello")
313
+ const triple = Tuple(true, 42, "hello")
314
+
315
+ // Accessing
316
+ pair.first() // 42
317
+ pair.second() // "hello"
318
+ triple.third() // "hello"
319
+ pair.toArray() // [42, "hello"]
320
+
321
+ // Transforming
322
+ pair.mapFirst((x) => x * 2) // Tuple(84, "hello")
323
+ pair.mapSecond((s) => s.toUpperCase()) // Tuple(42, "HELLO")
324
+ pair.map(([a, b]) => [a * 2, b.toUpperCase()]) // Tuple(84, "HELLO")
325
+
326
+ // Operations
327
+ pair.swap() // Tuple("hello", 42)
328
+ pair.apply((a, b) => a + b.length) // 47
329
+
330
+ // Combining
331
+ pair.concat(Tuple(true)) // Tuple(42, "hello", true)
332
+ ```
333
+
334
+ ## Branded Types
335
+
336
+ ```typescript
337
+ import { Brand } from "functype/branded"
338
+
339
+ // Type definitions
340
+ type UserId = Brand<string, "UserId">
341
+ type Email = Brand<string, "Email">
342
+ type PositiveInt = Brand<number, "PositiveInt">
343
+
344
+ // Factory functions with validation
345
+ const UserId = (id: string): UserId => {
346
+ if (!/^U\d{6}$/.test(id)) throw new Error("Invalid ID format")
347
+ return id as UserId
348
+ }
349
+
350
+ const PositiveInt = (n: number): PositiveInt => {
351
+ if (!Number.isInteger(n) || n <= 0) throw new Error("Not a positive integer")
352
+ return n as PositiveInt
353
+ }
354
+
355
+ // Usage
356
+ function getUserById(id: UserId): User {
357
+ /* ... */
358
+ }
359
+
360
+ // Type-safe calls
361
+ getUserById(UserId("U123456")) // Works
362
+ // getUserById("U123456") // Type error: string is not UserId
363
+ ```
364
+
365
+ ## Pattern Matching
366
+
367
+ ```typescript
368
+ import { Option, Either, Try, List, MatchableUtils } from "functype"
369
+
370
+ // Built-in pattern matching
371
+ option.match({
372
+ Some: (value) => `Found: ${value}`,
373
+ None: () => "Not found",
374
+ })
375
+
376
+ either.match({
377
+ Right: (value) => `Success: ${value}`,
378
+ Left: (error) => `Error: ${error}`,
379
+ })
380
+
381
+ list.match({
382
+ NonEmpty: (values) => `Values: ${values.join(", ")}`,
383
+ Empty: () => "No values",
384
+ })
385
+
386
+ // Custom pattern matching
387
+ const isPositive = MatchableUtils.when(
388
+ (n: number) => n > 0,
389
+ (n) => `Positive: ${n}`,
390
+ )
391
+
392
+ const isZero = MatchableUtils.when(
393
+ (n: number) => n === 0,
394
+ () => "Zero",
395
+ )
396
+
397
+ const isNegative = MatchableUtils.when(
398
+ (n: number) => n < 0,
399
+ (n) => `Negative: ${n}`,
400
+ )
401
+
402
+ const defaultCase = MatchableUtils.default((x: number) => `Default: ${x}`)
403
+
404
+ // Chain patterns with fallbacks
405
+ isPositive(42) ?? isZero(42) ?? isNegative(42) ?? defaultCase(42) // "Positive: 42"
406
+ ```
407
+
408
+ ## Common Conversions
409
+
410
+ ```typescript
411
+ import { Option, Either, Try, List, FoldableUtils } from "functype"
412
+
413
+ // Convert between types
414
+ FoldableUtils.toList(Option(42)) // List([42])
415
+ FoldableUtils.toList(Either.right(42)) // List([42])
416
+ FoldableUtils.toList(Try(() => 42)) // List([42])
417
+
418
+ FoldableUtils.toOption(List([1, 2, 3])) // Some(1)
419
+ FoldableUtils.toOption(Either.right(42)) // Some(42)
420
+ FoldableUtils.toOption(Try(() => 42)) // Some(42)
421
+
422
+ FoldableUtils.toEither(Option(42), "Empty") // Right(42)
423
+ FoldableUtils.toEither(
424
+ Try(() => 42),
425
+ "Failed",
426
+ ) // Right(42)
427
+
428
+ // Built-in conversions
429
+ option.toEither("No value") // Either.Right or Either.Left
430
+ either.toOption() // Option.Some or Option.None
431
+ tryVal.toEither() // Either.Right or Either.Left with error
432
+ tryVal.toOption() // Option.Some or Option.None
433
+ ```
434
+
435
+ ## Functional Composition
436
+
437
+ ```typescript
438
+ import { pipe } from "functype/pipe"
439
+
440
+ // Pipe operations for cleaner sequential transformations
441
+ const result = pipe(
442
+ Option("42"),
443
+ (opt) => opt.map((s) => s.trim()),
444
+ (opt) => opt.map((s) => parseInt(s, 10)),
445
+ (opt) => opt.filter((n) => !isNaN(n)),
446
+ (opt) => opt.map((n) => n * 2),
447
+ (opt) => opt.orElse(0),
448
+ ) // 84
449
+
450
+ // Cross-type transformations
451
+ const mixed = pipe(
452
+ Option("42"),
453
+ (opt) => opt.map((s) => parseInt(s, 10)),
454
+ (opt) => opt.toEither("Invalid number"),
455
+ (e) => e.map((n) => n * 2),
456
+ (e) =>
457
+ e.fold(
458
+ (err) => `Error: ${err}`,
459
+ (val) => `Result: ${val}`,
460
+ ),
461
+ ) // "Result: 84"
462
+ ```