ok-fp 0.0.1
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 +21 -0
- package/README.md +71 -0
- package/dist/either.d.mts +2 -0
- package/dist/either.mjs +204 -0
- package/dist/helpers-BGxhvbJN.d.mts +665 -0
- package/dist/option.d.mts +2 -0
- package/dist/option.mjs +177 -0
- package/dist/task.d.mts +16 -0
- package/dist/task.mjs +19 -0
- package/dist/validation.d.mts +16 -0
- package/dist/validation.mjs +19 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Paweł Maciejewski
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# OKFP
|
|
2
|
+
|
|
3
|
+
 
|
|
4
|
+
|
|
5
|
+
> Essential typed effects for TypeScript.
|
|
6
|
+
|
|
7
|
+
OKFP is a small, focused functional programming toolkit for TypeScript.
|
|
8
|
+
|
|
9
|
+
It provides a minimal set of **typed effects**: composable, type-safe wrappers for optional values, errors, and async computations.
|
|
10
|
+
|
|
11
|
+
## Status
|
|
12
|
+
|
|
13
|
+
OKFP is pre-1.0.
|
|
14
|
+
|
|
15
|
+
- ✅ Implemented: `Option`, `Either`
|
|
16
|
+
- 🚧 Planned before `v1.0.0`: `Validation`, `Task`, `TaskEither`
|
|
17
|
+
|
|
18
|
+
See: [ROADMAP.md](./ROADMAP.md)
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
Install with your package manager of choice:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install okfp
|
|
26
|
+
# or
|
|
27
|
+
# pnpm add okfp
|
|
28
|
+
# yarn add okfp
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Basic example
|
|
32
|
+
|
|
33
|
+
This example shows a small pipeline using `Option`.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { type Option, some, none } from "okfp/option";
|
|
37
|
+
|
|
38
|
+
const parseNumber = (input: string): Option<number> => {
|
|
39
|
+
const n = Number(input);
|
|
40
|
+
return Number.isFinite(n) ? some(n) : none();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const nonZero = (n: number): Option<number> => (n !== 0 ? some(n) : none());
|
|
44
|
+
|
|
45
|
+
const positive = (n: number): Option<number> => (n > 0 ? some(n) : none());
|
|
46
|
+
|
|
47
|
+
const reciprocal = (n: number): Option<number> => some(1 / n);
|
|
48
|
+
|
|
49
|
+
const compute = (input: string): Option<number> =>
|
|
50
|
+
parseNumber(input)
|
|
51
|
+
.flatMap(positive) // must be > 0
|
|
52
|
+
.flatMap(nonZero) // must not be 0
|
|
53
|
+
.flatMap(reciprocal) // compute 1 / n
|
|
54
|
+
.map((n) => n * 100); // scale result
|
|
55
|
+
|
|
56
|
+
// Usage (returns Option instances)
|
|
57
|
+
compute("4"); // returns Option.some(25)
|
|
58
|
+
compute("0"); // returns Option.none() (fails nonZero)
|
|
59
|
+
compute("-3"); // returns Option.none() (fails positive)
|
|
60
|
+
compute("abc"); // returns Option.none() (fails parsing)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Documentation
|
|
64
|
+
|
|
65
|
+
Full documentation and API reference are available in the docs site or the `docs/` folder.
|
|
66
|
+
|
|
67
|
+
[API docs](https://pwlmc.github.io/okfp/)
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as fromOption, c as tryCatch, d as Left, f as Right, i as fromNullable, l as Either, n as map3, o as left, r as sequence, s as right, t as map2, u as EitherV } from "./helpers-BGxhvbJN.mjs";
|
|
2
|
+
export { type Either, EitherV, Left, Right, fromNullable, fromOption, left, map2, map3, right, sequence, tryCatch };
|
package/dist/either.mjs
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
//#region src/either/either.ts
|
|
2
|
+
function createEither(value) {
|
|
3
|
+
const either = {
|
|
4
|
+
filterOrElse: (predicate, onLeft) => either.flatMap((right) => predicate(right) ? either : createEither({ left: onLeft() })),
|
|
5
|
+
map: (mapper) => either.flatMap((right) => createEither({ right: mapper(right) })),
|
|
6
|
+
orElse: (fallback) => either.match((left) => forceCast(fallback(left)), () => forceCast(either)),
|
|
7
|
+
ap: function(arg) {
|
|
8
|
+
return this.flatMap((fn) => arg.map((right) => fn(right)));
|
|
9
|
+
},
|
|
10
|
+
swap: () => either.match((left) => createEither({ right: left }), (right) => createEither({ left: right })),
|
|
11
|
+
zip: (eitherA) => either.map((value) => (a) => [value, a]).ap(eitherA),
|
|
12
|
+
flatten: function() {
|
|
13
|
+
return this.flatMap((value) => value);
|
|
14
|
+
},
|
|
15
|
+
flatMap: (mapper) => either.match(() => forceCast(either), (right) => forceCast(mapper(right))),
|
|
16
|
+
tap: (sideEffect) => {
|
|
17
|
+
either.match(() => {}, sideEffect);
|
|
18
|
+
return either;
|
|
19
|
+
},
|
|
20
|
+
match: (onLeft, onRight) => isLeft(value) ? onLeft(value.left) : onRight(value.right),
|
|
21
|
+
getOrElse: (fallback) => either.match((error) => fallback(error), (value) => value),
|
|
22
|
+
toResult: () => either.match((error) => ({
|
|
23
|
+
ok: false,
|
|
24
|
+
error
|
|
25
|
+
}), (value) => ({
|
|
26
|
+
ok: true,
|
|
27
|
+
value
|
|
28
|
+
}))
|
|
29
|
+
};
|
|
30
|
+
return either;
|
|
31
|
+
}
|
|
32
|
+
function isLeft(value) {
|
|
33
|
+
return typeof value === "object" && "left" in value;
|
|
34
|
+
}
|
|
35
|
+
function forceCast(either) {
|
|
36
|
+
return either;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/either/constructors.ts
|
|
41
|
+
/**
|
|
42
|
+
* Creates an Either containing an error value (Left).
|
|
43
|
+
*
|
|
44
|
+
* @typeParam E - The type of the error value
|
|
45
|
+
* @typeParam T - The type of the success value (defaults to never since this is an error)
|
|
46
|
+
* @param left - The error value to wrap
|
|
47
|
+
* @returns Either containing the error value on the Left side
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const error = left("Something went wrong"); // Either<string, never>
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
function left(left) {
|
|
55
|
+
return createEither({ left });
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Creates an Either containing a success value (Right).
|
|
59
|
+
*
|
|
60
|
+
* @typeParam T - The type of the success value
|
|
61
|
+
* @typeParam E - The type of the error value (defaults to never since this is a success)
|
|
62
|
+
* @param right - The success value to wrap
|
|
63
|
+
* @returns Either containing the success value on the Right side
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* const success = right(42); // Either<never, number>
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
function right(right) {
|
|
71
|
+
return createEither({ right });
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Creates an Either from a potentially nullable value.
|
|
75
|
+
* If the value is null or undefined, creates a Left with the provided error.
|
|
76
|
+
* If the value is present, creates a Right with the value.
|
|
77
|
+
*
|
|
78
|
+
* @typeParam E - The type of the error value
|
|
79
|
+
* @typeParam T - The type of the success value
|
|
80
|
+
* @param nullable - The potentially null/undefined value
|
|
81
|
+
* @param onNullish - Function that provides the error value when nullable is null/undefined
|
|
82
|
+
* @returns Either containing the value if present, or the error if null/undefined
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* fromNullable("hello", () => "empty") // Right("hello")
|
|
87
|
+
* fromNullable(null, () => "empty") // Left("empty")
|
|
88
|
+
* fromNullable(undefined, () => "empty") // Left("empty")
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
function fromNullable(nullable, onNullish) {
|
|
92
|
+
return nullable != null ? right(nullable) : left(onNullish());
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Safely executes a function that might throw, converting exceptions to Either.
|
|
96
|
+
*
|
|
97
|
+
* @typeParam E - The type of the processed error value
|
|
98
|
+
* @typeParam T - The type of the success value
|
|
99
|
+
* @param fn - The function to execute that might throw
|
|
100
|
+
* @param onThrow - Function that processes the caught error into the desired error type
|
|
101
|
+
* @returns Either containing the result if successful, or the processed error if thrown
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const parseNumber = (str: string) =>
|
|
106
|
+
* tryCatch(
|
|
107
|
+
* () => JSON.parse(str),
|
|
108
|
+
* (err) => `Parse error: ${err.message}`
|
|
109
|
+
* );
|
|
110
|
+
*
|
|
111
|
+
* parseNumber("42") // Right(42)
|
|
112
|
+
* parseNumber("invalid") // Left("Parse error: ...")
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
function tryCatch(fn, onThrow) {
|
|
116
|
+
try {
|
|
117
|
+
return right(fn());
|
|
118
|
+
} catch (err) {
|
|
119
|
+
return left(onThrow(err));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Creates an Either from an Option by providing an error value for the None case.
|
|
124
|
+
*
|
|
125
|
+
* @typeParam E - The type of the error value
|
|
126
|
+
* @typeParam T - The type of the success value
|
|
127
|
+
* @param opt - The Option to convert
|
|
128
|
+
* @param onNone - Function that provides the error value when Option is None
|
|
129
|
+
* @returns Either containing the Option's value if Some, or the error if None
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* fromOption(some(42), () => "empty") // Right(42)
|
|
134
|
+
* fromOption(none(), () => "empty") // Left("empty")
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
function fromOption(opt, onNone) {
|
|
138
|
+
return opt.match(() => left(onNone()), (value) => right(value));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/either/helpers.ts
|
|
143
|
+
/**
|
|
144
|
+
* Combines two Eithers using a mapping function.
|
|
145
|
+
* Returns the mapped result if both Eithers are Right, otherwise the first Left.
|
|
146
|
+
*
|
|
147
|
+
* @param eitherA - First Either to combine
|
|
148
|
+
* @param eitherB - Second Either to combine
|
|
149
|
+
* @param mapper - Function to combine both Right values
|
|
150
|
+
* @returns Either containing the mapped result, or the first Left
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* map2(right("John"), right("Doe"), (first, last) => `${first} ${last}`)
|
|
155
|
+
* // Right("John Doe")
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
function map2(eitherA, eitherB, mapper) {
|
|
159
|
+
return eitherA.map((a) => (b) => mapper(a, b)).ap(eitherB);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Combines three Eithers using a mapping function.
|
|
163
|
+
* Returns the mapped result if all Eithers are Right, otherwise the first Left.
|
|
164
|
+
*
|
|
165
|
+
* @param eitherA - First Either to combine
|
|
166
|
+
* @param eitherB - Second Either to combine
|
|
167
|
+
* @param eitherC - Third Either to combine
|
|
168
|
+
* @param mapper - Function to combine all three Right values
|
|
169
|
+
* @returns Either containing the mapped result, or the first Left
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```typescript
|
|
173
|
+
* map3(right(1), right(2), right(3), (a, b, c) => a + b + c)
|
|
174
|
+
* // Right(6)
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
function map3(eitherA, eitherB, eitherC, mapper) {
|
|
178
|
+
return eitherA.map((a) => (b) => (c) => mapper(a, b, c)).ap(eitherB).ap(eitherC);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Converts an array of Eithers into an Either of array.
|
|
182
|
+
* Returns Right with all values if all Eithers are Right, otherwise the first Left.
|
|
183
|
+
*
|
|
184
|
+
* @param eithers - Array of Eithers to sequence
|
|
185
|
+
* @returns Either containing array of all values, or the first Left
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* sequence([right(1), right(2), right(3)]) // Right([1, 2, 3])
|
|
190
|
+
* sequence([right(1), left("error")]) // Left("error")
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
function sequence(eithers) {
|
|
194
|
+
const out = [];
|
|
195
|
+
for (const either of eithers) {
|
|
196
|
+
const result = either.toResult();
|
|
197
|
+
if (!result.ok) return left(result.error);
|
|
198
|
+
out.push(result.value);
|
|
199
|
+
}
|
|
200
|
+
return right(out);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
//#endregion
|
|
204
|
+
export { fromNullable, fromOption, left, map2, map3, right, sequence, tryCatch };
|
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
//#region src/option/model.d.ts
|
|
2
|
+
type Some<T> = {
|
|
3
|
+
some: T;
|
|
4
|
+
};
|
|
5
|
+
type None = symbol;
|
|
6
|
+
type OptionV<T> = Some<T> | None;
|
|
7
|
+
declare const NONE: unique symbol;
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/option/option.d.ts
|
|
10
|
+
type Option<T> = {
|
|
11
|
+
/**
|
|
12
|
+
* Filters the Option based on a predicate function.
|
|
13
|
+
* If this Option is Some and the predicate returns true, returns this Option.
|
|
14
|
+
* Otherwise, returns None.
|
|
15
|
+
*
|
|
16
|
+
* @param predicate - Function that tests the contained value
|
|
17
|
+
* @returns The same Option if predicate passes, None otherwise
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* some(5).filter(x => x > 3) // Some(5)
|
|
22
|
+
* some(2).filter(x => x > 3) // None
|
|
23
|
+
* none().filter(x => x > 3) // None
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
filter: (predicate: (value: T) => boolean) => Option<T>;
|
|
27
|
+
/**
|
|
28
|
+
* Transforms the value inside the Option using a mapping function.
|
|
29
|
+
* If this Option is None, returns None without calling the mapper.
|
|
30
|
+
*
|
|
31
|
+
* @param mapper - Function to transform the contained value
|
|
32
|
+
* @returns New Option containing the transformed value, or None
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* some(5).map(x => x * 2) // Some(10)
|
|
37
|
+
* none<number>().map(x => x * 2) // None
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
map: <U>(mapper: (value: T) => U) => Option<U>;
|
|
41
|
+
/**
|
|
42
|
+
* Returns this Option if it contains a value, otherwise returns the fallback Option.
|
|
43
|
+
* The fallback is lazily evaluated to avoid unnecessary computation.
|
|
44
|
+
*
|
|
45
|
+
* @param fallback - Function that provides the alternative Option
|
|
46
|
+
* @returns This Option if Some, otherwise the fallback Option
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* some(5).orElse(() => some(10)) // Some(5)
|
|
51
|
+
* none().orElse(() => some(10)) // Some(10)
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
orElse: (fallback: () => Option<T>) => Option<T>;
|
|
55
|
+
/**
|
|
56
|
+
* Applies a function wrapped in an Option to a value wrapped in an Option.
|
|
57
|
+
* This is useful for applying functions that are also optional.
|
|
58
|
+
*
|
|
59
|
+
* @param optA - Option containing the argument to apply the function to
|
|
60
|
+
* @returns Option containing the result, or None if either Option is None
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const add = (x: number) => (y: number) => x + y;
|
|
65
|
+
* some(add(5)).ap(some(3)) // Some(8)
|
|
66
|
+
* some(add(5)).ap(none()) // None
|
|
67
|
+
* none().ap(some(3)) // None
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
ap: <A, U>(this: Option<(arg: A) => U>, optA: Option<A>) => Option<U>;
|
|
71
|
+
/**
|
|
72
|
+
* Combines this Option with another Option into a tuple.
|
|
73
|
+
* If both Options contain values, returns Some containing a tuple of both values.
|
|
74
|
+
* If either Option is None, returns None.
|
|
75
|
+
*
|
|
76
|
+
* @param optA - The Option to combine with this one
|
|
77
|
+
* @returns Option containing a tuple of both values, or None if either is None
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* const name = some("Alice");
|
|
82
|
+
* const age = some(30);
|
|
83
|
+
* name.zip(age) // Some(["Alice", 30])
|
|
84
|
+
*
|
|
85
|
+
* const missing = none<number>();
|
|
86
|
+
* name.zip(missing) // None
|
|
87
|
+
* none().zip(age) // None
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
zip: <A>(optA: Option<A>) => Option<readonly [T, A]>;
|
|
91
|
+
/**
|
|
92
|
+
* Flattens a nested Option structure by removing one level of nesting.
|
|
93
|
+
* If this Option contains another Option, returns the inner Option.
|
|
94
|
+
* If this Option is None, returns None.
|
|
95
|
+
*
|
|
96
|
+
* @returns The inner Option if this Option contains one, or None
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* some(some(5)).flatten() // Some(5)
|
|
101
|
+
* some(none<number>()).flatten() // None
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
flatten: <U>(this: Option<Option<U>>) => Option<U>;
|
|
105
|
+
/**
|
|
106
|
+
* Chains Option-returning operations together (monadic bind).
|
|
107
|
+
* If this Option is Some, applies the mapper and returns the result.
|
|
108
|
+
* If this Option is None, returns None without calling the mapper.
|
|
109
|
+
*
|
|
110
|
+
* @param mapper - Function that returns an Option
|
|
111
|
+
* @returns The Option returned by mapper, or None
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const safeDivide = (x: number) => x === 0 ? none() : some(10 / x);
|
|
116
|
+
* some(2).flatMap(safeDivide) // Some(5)
|
|
117
|
+
* some(0).flatMap(safeDivide) // None
|
|
118
|
+
* none().flatMap(safeDivide) // None
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
flatMap: <U>(mapper: (value: T) => Option<U>) => Option<U>;
|
|
122
|
+
/**
|
|
123
|
+
* Performs a side effect if this Option contains a value.
|
|
124
|
+
* Returns the original Option unchanged.
|
|
125
|
+
*
|
|
126
|
+
* @returns The same Option instance
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```typescript
|
|
130
|
+
* some(5).tap((value) => console.log("Has value", value)) // Some(5), logs message
|
|
131
|
+
* none().tap((value) => console.log("Has value", value)) // None, no log
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
tap: (sideEffect: (value: T) => unknown) => Option<T>;
|
|
135
|
+
/**
|
|
136
|
+
* Performs a side effect if this Option is None.
|
|
137
|
+
* Returns the original Option unchanged.
|
|
138
|
+
*
|
|
139
|
+
* @returns The same Option instance
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* some(5).tapNone(() => console.log("No value")) // Some(5), no log
|
|
144
|
+
* none().tapNone(() => console.log("No value")) // None, logs message
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
tapNone: (sideEffect: () => unknown) => Option<T>;
|
|
148
|
+
/**
|
|
149
|
+
* Pattern matches on the Option, executing different functions based on its state.
|
|
150
|
+
*
|
|
151
|
+
* @param onNone - Function to execute if Option is None
|
|
152
|
+
* @param onSome - Function to execute if Option is Some
|
|
153
|
+
* @returns The result of the executed function
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* some(5).match(() => "empty", x => `value: ${x}`) // "value: 5"
|
|
158
|
+
* none().match(() => "empty", x => `value: ${x}`) // "empty"
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
match: <U>(onNone: () => U, onSome: (value: T) => U) => U;
|
|
162
|
+
/**
|
|
163
|
+
* Extracts the value from the Option, or returns a fallback value if None.
|
|
164
|
+
*
|
|
165
|
+
* @param fallback - Function that provides the default value
|
|
166
|
+
* @returns The value or the fallback value
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* some(5).getOrElse(() => 0) // 5
|
|
171
|
+
* none().getOrElse(() => 0) // 0
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
getOrElse: (fallback: () => T) => T;
|
|
175
|
+
/**
|
|
176
|
+
* Converts the Option to a nullable value.
|
|
177
|
+
* Returns the contained value if Some, or null if None.
|
|
178
|
+
*
|
|
179
|
+
* @returns The value or null
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* some(5).toNullable() // 5
|
|
184
|
+
* none().toNullable() // null
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
toNullable: () => T | null;
|
|
188
|
+
/**
|
|
189
|
+
* Converts the Option to an array.
|
|
190
|
+
* If this Option contains a value, returns an array with that single value.
|
|
191
|
+
* If this Option is None, returns an empty array.
|
|
192
|
+
*
|
|
193
|
+
* @returns Array containing the value if Some, or empty array if None
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* some(42).toArray() // [42]
|
|
198
|
+
* none().toArray() // []
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
toArray: () => readonly T[];
|
|
202
|
+
};
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/option/constructors.d.ts
|
|
205
|
+
/**
|
|
206
|
+
* Creates an {@link Option} that contains a value.
|
|
207
|
+
*
|
|
208
|
+
* Use this to wrap an existing value into an `Option<T>` representing presence
|
|
209
|
+
* (as opposed to `none`, which represents absence).
|
|
210
|
+
*
|
|
211
|
+
* @typeParam T - Type of the wrapped value.
|
|
212
|
+
* @param some - The value to store in the option.
|
|
213
|
+
* @returns An {@link Option} containing the provided value.
|
|
214
|
+
*/
|
|
215
|
+
declare function some<T>(some: T): Option<T>;
|
|
216
|
+
/**
|
|
217
|
+
* Creates an {@link Option} representing the absence of a value.
|
|
218
|
+
*
|
|
219
|
+
* @typeParam T - The type of the value that would be contained if present.
|
|
220
|
+
* @returns An {@link Option} in the `None` state.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* const value = none<number>();
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
declare function none<T>(): Option<T>;
|
|
228
|
+
/**
|
|
229
|
+
* Creates an {@link Option} from a nullable value.
|
|
230
|
+
*
|
|
231
|
+
* If {@link nullable} is `null` or `undefined`, returns {@link none}.
|
|
232
|
+
* Otherwise, wraps the provided value in {@link some}.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* fromNullable(0).getOrElse(() => 123); // 0
|
|
236
|
+
* fromNullable(null).toNullable(); // null
|
|
237
|
+
*
|
|
238
|
+
* @typeParam T - The non-null value type to wrap.
|
|
239
|
+
* @param nullable - A value that may be `null` or `undefined`.
|
|
240
|
+
* @returns An {@link Option} that is `Some` when the value is present, otherwise `None`.
|
|
241
|
+
*/
|
|
242
|
+
declare function fromNullable$1<T>(nullable: null | undefined | T): Option<T>;
|
|
243
|
+
/**
|
|
244
|
+
* Creates an {@link Option} from an {@link Either} by extracting the Right value.
|
|
245
|
+
*
|
|
246
|
+
* If the Either is Right, returns Some containing the Right value.
|
|
247
|
+
* If the Either is Left, returns None (discarding the Left value).
|
|
248
|
+
*
|
|
249
|
+
* @typeParam L - The type of the Left value (error type) that will be discarded
|
|
250
|
+
* @typeParam R - The type of the Right value that will be preserved in the Option
|
|
251
|
+
* @param either - The Either to convert to an Option
|
|
252
|
+
* @returns Some containing the Right value, or None if the Either was Left
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```typescript
|
|
256
|
+
* const success = right(42);
|
|
257
|
+
* fromEither(success) // Some(42)
|
|
258
|
+
*
|
|
259
|
+
* const failure = left("error message");
|
|
260
|
+
* fromEither(failure) // None
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
declare function fromEither<L, R>(either: Either<L, R>): Option<R>;
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region src/option/helpers.d.ts
|
|
266
|
+
/**
|
|
267
|
+
* Combines two Options using a mapping function.
|
|
268
|
+
* Only applies the mapper if both Options contain values.
|
|
269
|
+
*
|
|
270
|
+
* @param optA - First Option to combine
|
|
271
|
+
* @param optB - Second Option to combine
|
|
272
|
+
* @param mapper - Function that combines both values
|
|
273
|
+
* @returns Option containing the combined result, or None if either Option is None
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```typescript
|
|
277
|
+
* const greet = (first, last) => `Hi ${first} ${last}!`
|
|
278
|
+
* map2(some("John"), some("Doe"), greet) // Some("John Doe")
|
|
279
|
+
* map2(some("John"), none<string>(), greet) // None
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
declare function map2$1<A, B, C>(optA: Option<A>, optB: Option<B>, mapper: (a: A, b: B) => C): Option<C>;
|
|
283
|
+
/**
|
|
284
|
+
* Combines three Options using a mapping function.
|
|
285
|
+
* Only applies the mapper if all three Options contain values.
|
|
286
|
+
*
|
|
287
|
+
* @param optA - First Option to combine
|
|
288
|
+
* @param optB - Second Option to combine
|
|
289
|
+
* @param optC - Third Option to combine
|
|
290
|
+
* @param mapper - Function that combines all three values
|
|
291
|
+
* @returns Option containing the combined result, or None if any Option is None
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```typescript
|
|
295
|
+
* const toDateString = (d, m, y) => `${d}/${m}/${y}`
|
|
296
|
+
* map3(some(15), some(6), some(2023), toDateString) // Some("15/6/2023")
|
|
297
|
+
* map3(some(15), some(6), none<number>(), toDateString) // None
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
declare function map3$1<A, B, C, D>(optA: Option<A>, optB: Option<B>, optC: Option<C>, mapper: (a: A, b: B, c: C) => D): Option<D>;
|
|
301
|
+
/**
|
|
302
|
+
* Converts an array of Options into an Option of array.
|
|
303
|
+
* If all Options in the array contain values, returns Some containing an array of all values.
|
|
304
|
+
* If any Option in the array is None, returns None.
|
|
305
|
+
*
|
|
306
|
+
* @param opts - Array of Options to sequence
|
|
307
|
+
* @returns Some containing array of all values if all Options are Some, otherwise None
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```typescript
|
|
311
|
+
* sequence([some(1), some(2), some(3)]) // Some([1, 2, 3])
|
|
312
|
+
* sequence([some(1), none(), some(3)]) // None
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
declare function sequence$1<T>(opts: Option<T>[]): Option<T[]>;
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/either/model.d.ts
|
|
318
|
+
type Left<E> = {
|
|
319
|
+
readonly left: E;
|
|
320
|
+
};
|
|
321
|
+
type Right<T> = {
|
|
322
|
+
readonly right: T;
|
|
323
|
+
};
|
|
324
|
+
type EitherV<E, T> = Left<E> | Right<T>;
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region src/either/either.d.ts
|
|
327
|
+
type Result<E, T> = {
|
|
328
|
+
ok: true;
|
|
329
|
+
value: T;
|
|
330
|
+
} | {
|
|
331
|
+
ok: false;
|
|
332
|
+
error: E;
|
|
333
|
+
};
|
|
334
|
+
type Either<E, T> = {
|
|
335
|
+
/**
|
|
336
|
+
* Filters the Either based on a predicate function applied to the Right value.
|
|
337
|
+
*
|
|
338
|
+
* @param predicate - Function that tests the Right value
|
|
339
|
+
* @param onLeft - Function that provides the error value when predicate fails
|
|
340
|
+
* @returns The same Either if Left or predicate passes, otherwise Left with the provided error
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* ```typescript
|
|
344
|
+
* const isPositive = (n: number) => n > 0;
|
|
345
|
+
* right(5).filterOrElse(isPositive, () => "Must be positive") // Right(5)
|
|
346
|
+
* right(-3).filterOrElse(isPositive, () => "Must be positive") // Left("Must be positive")
|
|
347
|
+
* left("error").filterOrElse(isPositive, () => "Must be positive") // Left("error")
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
filterOrElse: (predicate: (right: T) => boolean, onLeft: () => E) => Either<E, T>;
|
|
351
|
+
/**
|
|
352
|
+
* Transforms the Right value using a mapping function.
|
|
353
|
+
*
|
|
354
|
+
* @typeParam U - The type of the transformed value
|
|
355
|
+
* @param mapper - Function to transform the Right value
|
|
356
|
+
* @returns New Either with the transformed Right value, or the same Left if error
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```typescript
|
|
360
|
+
* right(5).map(x => x * 2) // Right(10)
|
|
361
|
+
* left("error").map(x => x * 2) // Left("error")
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
map: <U>(mapper: (right: T) => U) => Either<E, U>;
|
|
365
|
+
/**
|
|
366
|
+
* Returns this Either if it's Right, otherwise returns the result of the fallback function.
|
|
367
|
+
*
|
|
368
|
+
* @typeParam EE - The type of the error in the fallback Either
|
|
369
|
+
* @param fallback - Function that takes the Left value and returns an alternative Either
|
|
370
|
+
* @returns This Either if Right, otherwise the Either returned by the fallback function
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```typescript
|
|
374
|
+
* right(42).orElse((err) => right(0)) // Right(42)
|
|
375
|
+
* left("error").orElse((err) => right(0)) // Right(0)
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
orElse: <EE>(fallback: (left: E) => Either<EE, T>) => Either<E | EE, T>;
|
|
379
|
+
/**
|
|
380
|
+
* Applies a function wrapped in an Either to a value wrapped in an Either.
|
|
381
|
+
*
|
|
382
|
+
* @typeParam A - The type of the argument value
|
|
383
|
+
* @typeParam U - The type of the function's return value
|
|
384
|
+
* @param arg - Either containing the argument to apply the function to
|
|
385
|
+
* @returns Either containing the function result, or the first Left if any Either is Left
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* ```typescript
|
|
389
|
+
* const add = (x: number) => (y: number) => x + y;
|
|
390
|
+
* right(add(5)).ap(right(3)) // Right(8)
|
|
391
|
+
* right(add(5)).ap(left("err")) // Left("err")
|
|
392
|
+
* left("err").ap(right(3)) // Left("err")
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
395
|
+
ap: <EE, A, U>(this: Either<E, (a: A) => U>, arg: Either<EE, A>) => Either<E | EE, U>;
|
|
396
|
+
/**
|
|
397
|
+
* Swaps the Left and Right sides of the Either.
|
|
398
|
+
*
|
|
399
|
+
* @returns Either with Left and Right sides swapped
|
|
400
|
+
*
|
|
401
|
+
* @example
|
|
402
|
+
* ```typescript
|
|
403
|
+
* right(42).swap() // Left(42)
|
|
404
|
+
* left("error").swap() // Right("error")
|
|
405
|
+
* ```
|
|
406
|
+
*/
|
|
407
|
+
swap: () => Either<T, E>;
|
|
408
|
+
/**
|
|
409
|
+
* Combines this Either with another Either into a tuple.
|
|
410
|
+
*
|
|
411
|
+
* @typeParam EE - Left type of the Either to combine with
|
|
412
|
+
* @typeParam A - Right type of the Either to combine with
|
|
413
|
+
* @param eitherA - The Either to combine with this one
|
|
414
|
+
* @returns Either containing a tuple of both Right values, or the first Left if any Either is Left
|
|
415
|
+
*
|
|
416
|
+
* @example
|
|
417
|
+
* ```typescript
|
|
418
|
+
* right("Alice").zip(right(30)) // Right(["Alice", 30])
|
|
419
|
+
* right("Alice").zip(left("No age")) // Left("No age")
|
|
420
|
+
* left("No name").zip(right(30)) // Left("No name")
|
|
421
|
+
* ```
|
|
422
|
+
*/
|
|
423
|
+
zip: <EE, A>(eitherA: Either<EE, A>) => Either<E | EE, readonly [T, A]>;
|
|
424
|
+
/**
|
|
425
|
+
* Flattens a nested Either structure by removing one level of nesting.
|
|
426
|
+
*
|
|
427
|
+
* @typeParam EE - Left type of the inner Either
|
|
428
|
+
* @typeParam U - Right type of the inner Either
|
|
429
|
+
* @returns The inner Either if this Either is Right, otherwise this Either unchanged
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* ```typescript
|
|
433
|
+
* right(right(42)).flatten() // Right(42)
|
|
434
|
+
* right(left("inner error")).flatten() // Left("inner error")
|
|
435
|
+
*
|
|
436
|
+
* ```
|
|
437
|
+
*/
|
|
438
|
+
flatten: <EE, U>(this: Either<E, Either<EE, U>>) => Either<E | EE, U>;
|
|
439
|
+
/**
|
|
440
|
+
* Chains Either-returning operations together (monadic bind).
|
|
441
|
+
*
|
|
442
|
+
* @typeParam EE - The error type of the Either returned by the mapper
|
|
443
|
+
* @typeParam U - The success type of the Either returned by the mapper
|
|
444
|
+
* @param mapper - Function that takes a Right value and returns an Either
|
|
445
|
+
* @returns The Either returned by mapper if this Either is Right, otherwise this Either unchanged
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* ```typescript
|
|
449
|
+
* const safeDivide = (x: number, y: number) =>
|
|
450
|
+
* y === 0 ? left("Division by zero") : right(x / y);
|
|
451
|
+
*
|
|
452
|
+
* right(10).flatMap(x => safeDivide(x, 2)) // Right(5)
|
|
453
|
+
* right(10).flatMap(x => safeDivide(x, 0)) // Left("Division by zero")
|
|
454
|
+
* left("error").flatMap(x => safeDivide(x, 2)) // Left("error") - mapper not called
|
|
455
|
+
* ```
|
|
456
|
+
*/
|
|
457
|
+
flatMap: <EE, U>(mapper: (right: T) => Either<EE, U>) => Either<E | EE, U>;
|
|
458
|
+
/**
|
|
459
|
+
* Performs a side effect if this Either is Right, returning the original Either unchanged.
|
|
460
|
+
* If this Either is Left, the side effect is not executed and the Either is returned as-is.
|
|
461
|
+
*
|
|
462
|
+
* @param sideEffect - Function to execute with the Right value (return value is ignored)
|
|
463
|
+
* @returns The same Either instance unchanged
|
|
464
|
+
*
|
|
465
|
+
* @example
|
|
466
|
+
* ```typescript
|
|
467
|
+
* right(42)
|
|
468
|
+
* .tap(value => console.log(`Got value: ${value}`)) // Logs: "Got value: 42"
|
|
469
|
+
* left("error")
|
|
470
|
+
* .tap(value => console.log(`Got value: ${value}`)) // No log output
|
|
471
|
+
*
|
|
472
|
+
* ```
|
|
473
|
+
*/
|
|
474
|
+
tap: (sideEffect: (right: T) => void) => Either<E, T>;
|
|
475
|
+
/**
|
|
476
|
+
* Pattern matches on the Either, executing different functions based on its state.
|
|
477
|
+
*
|
|
478
|
+
* @typeParam U - The return type of both matcher functions
|
|
479
|
+
* @param onLeft - Function to execute if Either is Left, receives the Left value
|
|
480
|
+
* @param onRight - Function to execute if Either is Right, receives the Right value
|
|
481
|
+
* @returns The result of the executed function
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
* ```typescript
|
|
485
|
+
* right(42).match(
|
|
486
|
+
* (err) => `Error: ${err}`,
|
|
487
|
+
* (val) => `Value: ${val}`
|
|
488
|
+
* ) // "Value: 42"
|
|
489
|
+
*
|
|
490
|
+
* left("Not a number").match(
|
|
491
|
+
* (err) => `Error: ${err}`,
|
|
492
|
+
* (val) => `Value: ${val}`
|
|
493
|
+
* ) // "Error: Not a number"
|
|
494
|
+
* ```
|
|
495
|
+
*/
|
|
496
|
+
match: <U>(onLeft: (left: E) => U, onRight: (right: T) => U) => U;
|
|
497
|
+
/**
|
|
498
|
+
* Extracts the Right value from the Either, or returns a fallback value if Left.
|
|
499
|
+
*
|
|
500
|
+
* @param fallback - Function that takes the Left value and returns a default value of type T
|
|
501
|
+
* @returns The Right value if present, otherwise the result of the fallback function
|
|
502
|
+
*
|
|
503
|
+
* @example
|
|
504
|
+
* ```typescript
|
|
505
|
+
* right(42).getOrElse((err) => 0) // 42
|
|
506
|
+
* left("error").getOrElse((err) => 0) // 0
|
|
507
|
+
* ```
|
|
508
|
+
*/
|
|
509
|
+
getOrElse: (fallback: (left: E) => T) => T;
|
|
510
|
+
/**
|
|
511
|
+
* Converts the Either to a Result type, which is an object with `ok` and `error` or `value` properties.
|
|
512
|
+
*
|
|
513
|
+
* @returns A Result object representing the Either's state
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
* ```typescript
|
|
517
|
+
* const result = right(42).toResult();
|
|
518
|
+
* console.log(result.ok); // true
|
|
519
|
+
* console.log(result.value); // 42
|
|
520
|
+
*
|
|
521
|
+
* const errorResult = left("error").toResult();
|
|
522
|
+
* console.log(errorResult.ok); // false
|
|
523
|
+
* console.log(errorResult.error); // "error"
|
|
524
|
+
* ```
|
|
525
|
+
*/
|
|
526
|
+
toResult: () => Result<E, T>;
|
|
527
|
+
};
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region src/either/constructors.d.ts
|
|
530
|
+
/**
|
|
531
|
+
* Creates an Either containing an error value (Left).
|
|
532
|
+
*
|
|
533
|
+
* @typeParam E - The type of the error value
|
|
534
|
+
* @typeParam T - The type of the success value (defaults to never since this is an error)
|
|
535
|
+
* @param left - The error value to wrap
|
|
536
|
+
* @returns Either containing the error value on the Left side
|
|
537
|
+
*
|
|
538
|
+
* @example
|
|
539
|
+
* ```typescript
|
|
540
|
+
* const error = left("Something went wrong"); // Either<string, never>
|
|
541
|
+
* ```
|
|
542
|
+
*/
|
|
543
|
+
declare function left<E, T = never>(left: E): Either<E, T>;
|
|
544
|
+
/**
|
|
545
|
+
* Creates an Either containing a success value (Right).
|
|
546
|
+
*
|
|
547
|
+
* @typeParam T - The type of the success value
|
|
548
|
+
* @typeParam E - The type of the error value (defaults to never since this is a success)
|
|
549
|
+
* @param right - The success value to wrap
|
|
550
|
+
* @returns Either containing the success value on the Right side
|
|
551
|
+
*
|
|
552
|
+
* @example
|
|
553
|
+
* ```typescript
|
|
554
|
+
* const success = right(42); // Either<never, number>
|
|
555
|
+
* ```
|
|
556
|
+
*/
|
|
557
|
+
declare function right<T, E = never>(right: T): Either<E, T>;
|
|
558
|
+
/**
|
|
559
|
+
* Creates an Either from a potentially nullable value.
|
|
560
|
+
* If the value is null or undefined, creates a Left with the provided error.
|
|
561
|
+
* If the value is present, creates a Right with the value.
|
|
562
|
+
*
|
|
563
|
+
* @typeParam E - The type of the error value
|
|
564
|
+
* @typeParam T - The type of the success value
|
|
565
|
+
* @param nullable - The potentially null/undefined value
|
|
566
|
+
* @param onNullish - Function that provides the error value when nullable is null/undefined
|
|
567
|
+
* @returns Either containing the value if present, or the error if null/undefined
|
|
568
|
+
*
|
|
569
|
+
* @example
|
|
570
|
+
* ```typescript
|
|
571
|
+
* fromNullable("hello", () => "empty") // Right("hello")
|
|
572
|
+
* fromNullable(null, () => "empty") // Left("empty")
|
|
573
|
+
* fromNullable(undefined, () => "empty") // Left("empty")
|
|
574
|
+
* ```
|
|
575
|
+
*/
|
|
576
|
+
declare function fromNullable<E, T>(nullable: T | null | undefined, onNullish: () => E): Either<E, T>;
|
|
577
|
+
/**
|
|
578
|
+
* Safely executes a function that might throw, converting exceptions to Either.
|
|
579
|
+
*
|
|
580
|
+
* @typeParam E - The type of the processed error value
|
|
581
|
+
* @typeParam T - The type of the success value
|
|
582
|
+
* @param fn - The function to execute that might throw
|
|
583
|
+
* @param onThrow - Function that processes the caught error into the desired error type
|
|
584
|
+
* @returns Either containing the result if successful, or the processed error if thrown
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* ```typescript
|
|
588
|
+
* const parseNumber = (str: string) =>
|
|
589
|
+
* tryCatch(
|
|
590
|
+
* () => JSON.parse(str),
|
|
591
|
+
* (err) => `Parse error: ${err.message}`
|
|
592
|
+
* );
|
|
593
|
+
*
|
|
594
|
+
* parseNumber("42") // Right(42)
|
|
595
|
+
* parseNumber("invalid") // Left("Parse error: ...")
|
|
596
|
+
* ```
|
|
597
|
+
*/
|
|
598
|
+
declare function tryCatch<E, T>(fn: () => T, onThrow: (err: unknown) => E): Either<E, T>;
|
|
599
|
+
/**
|
|
600
|
+
* Creates an Either from an Option by providing an error value for the None case.
|
|
601
|
+
*
|
|
602
|
+
* @typeParam E - The type of the error value
|
|
603
|
+
* @typeParam T - The type of the success value
|
|
604
|
+
* @param opt - The Option to convert
|
|
605
|
+
* @param onNone - Function that provides the error value when Option is None
|
|
606
|
+
* @returns Either containing the Option's value if Some, or the error if None
|
|
607
|
+
*
|
|
608
|
+
* @example
|
|
609
|
+
* ```typescript
|
|
610
|
+
* fromOption(some(42), () => "empty") // Right(42)
|
|
611
|
+
* fromOption(none(), () => "empty") // Left("empty")
|
|
612
|
+
* ```
|
|
613
|
+
*/
|
|
614
|
+
declare function fromOption<E, T>(opt: Option<T>, onNone: () => E): Either<E, T>;
|
|
615
|
+
//#endregion
|
|
616
|
+
//#region src/either/helpers.d.ts
|
|
617
|
+
/**
|
|
618
|
+
* Combines two Eithers using a mapping function.
|
|
619
|
+
* Returns the mapped result if both Eithers are Right, otherwise the first Left.
|
|
620
|
+
*
|
|
621
|
+
* @param eitherA - First Either to combine
|
|
622
|
+
* @param eitherB - Second Either to combine
|
|
623
|
+
* @param mapper - Function to combine both Right values
|
|
624
|
+
* @returns Either containing the mapped result, or the first Left
|
|
625
|
+
*
|
|
626
|
+
* @example
|
|
627
|
+
* ```typescript
|
|
628
|
+
* map2(right("John"), right("Doe"), (first, last) => `${first} ${last}`)
|
|
629
|
+
* // Right("John Doe")
|
|
630
|
+
* ```
|
|
631
|
+
*/
|
|
632
|
+
declare function map2<EA, A, EB, B, C>(eitherA: Either<EA, A>, eitherB: Either<EB, B>, mapper: (a: A, b: B) => C): Either<EA | EB, C>;
|
|
633
|
+
/**
|
|
634
|
+
* Combines three Eithers using a mapping function.
|
|
635
|
+
* Returns the mapped result if all Eithers are Right, otherwise the first Left.
|
|
636
|
+
*
|
|
637
|
+
* @param eitherA - First Either to combine
|
|
638
|
+
* @param eitherB - Second Either to combine
|
|
639
|
+
* @param eitherC - Third Either to combine
|
|
640
|
+
* @param mapper - Function to combine all three Right values
|
|
641
|
+
* @returns Either containing the mapped result, or the first Left
|
|
642
|
+
*
|
|
643
|
+
* @example
|
|
644
|
+
* ```typescript
|
|
645
|
+
* map3(right(1), right(2), right(3), (a, b, c) => a + b + c)
|
|
646
|
+
* // Right(6)
|
|
647
|
+
* ```
|
|
648
|
+
*/
|
|
649
|
+
declare function map3<EA, A, EB, B, EC, C, D>(eitherA: Either<EA, A>, eitherB: Either<EB, B>, eitherC: Either<EC, C>, mapper: (a: A, b: B, c: C) => D): Either<EA | EB | EC, D>;
|
|
650
|
+
/**
|
|
651
|
+
* Converts an array of Eithers into an Either of array.
|
|
652
|
+
* Returns Right with all values if all Eithers are Right, otherwise the first Left.
|
|
653
|
+
*
|
|
654
|
+
* @param eithers - Array of Eithers to sequence
|
|
655
|
+
* @returns Either containing array of all values, or the first Left
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```typescript
|
|
659
|
+
* sequence([right(1), right(2), right(3)]) // Right([1, 2, 3])
|
|
660
|
+
* sequence([right(1), left("error")]) // Left("error")
|
|
661
|
+
* ```
|
|
662
|
+
*/
|
|
663
|
+
declare function sequence<E, T>(eithers: Either<E, T>[]): Either<E, T[]>;
|
|
664
|
+
//#endregion
|
|
665
|
+
export { OptionV as C, None as S, fromNullable$1 as _, fromOption as a, Option as b, tryCatch as c, Left as d, Right as f, fromEither as g, sequence$1 as h, fromNullable as i, Either as l, map3$1 as m, map3 as n, left as o, map2$1 as p, sequence as r, right as s, map2 as t, EitherV as u, none as v, Some as w, NONE as x, some as y };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as OptionV, S as None, _ as fromNullable, b as Option, g as fromEither, h as sequence, m as map3, p as map2, v as none, w as Some, x as NONE, y as some } from "./helpers-BGxhvbJN.mjs";
|
|
2
|
+
export { NONE, None, type Option, OptionV, Some, fromEither, fromNullable, map2, map3, none, sequence, some };
|
package/dist/option.mjs
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
//#region src/option/model.ts
|
|
2
|
+
const NONE = Symbol("None");
|
|
3
|
+
|
|
4
|
+
//#endregion
|
|
5
|
+
//#region src/option/option.ts
|
|
6
|
+
function createOption(optionValue) {
|
|
7
|
+
const option = {
|
|
8
|
+
filter: (predicate) => option.flatMap((value) => predicate(value) ? option : createOption(NONE)),
|
|
9
|
+
map: (mapper) => option.flatMap((value) => createOption({ some: mapper(value) })),
|
|
10
|
+
orElse: (fallback) => option.match(fallback, () => option),
|
|
11
|
+
ap: function(optA) {
|
|
12
|
+
return this.flatMap((fn) => optA.match(() => createOption(NONE), (arg) => createOption({ some: fn(arg) })));
|
|
13
|
+
},
|
|
14
|
+
zip: (optA) => option.map((value) => (a) => [value, a]).ap(optA),
|
|
15
|
+
flatten: function() {
|
|
16
|
+
return this.flatMap((value) => value);
|
|
17
|
+
},
|
|
18
|
+
flatMap: (mapper) => option.match(() => forceCast(option), (value) => mapper(value)),
|
|
19
|
+
tap: (sideEffect) => {
|
|
20
|
+
option.match(() => {}, sideEffect);
|
|
21
|
+
return option;
|
|
22
|
+
},
|
|
23
|
+
tapNone: (sideEffect) => {
|
|
24
|
+
option.match(sideEffect, () => {});
|
|
25
|
+
return option;
|
|
26
|
+
},
|
|
27
|
+
match: (onNone, onSome) => isSome(optionValue) ? onSome(optionValue.some) : onNone(),
|
|
28
|
+
getOrElse: (fallback) => option.match(fallback, (value) => value),
|
|
29
|
+
toNullable: () => option.match(() => null, (value) => value),
|
|
30
|
+
toArray: () => option.match(() => [], (val) => [val])
|
|
31
|
+
};
|
|
32
|
+
return option;
|
|
33
|
+
}
|
|
34
|
+
function isSome(option) {
|
|
35
|
+
return typeof option === "object" && "some" in option;
|
|
36
|
+
}
|
|
37
|
+
function forceCast(option) {
|
|
38
|
+
return option;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/option/constructors.ts
|
|
43
|
+
/**
|
|
44
|
+
* Creates an {@link Option} that contains a value.
|
|
45
|
+
*
|
|
46
|
+
* Use this to wrap an existing value into an `Option<T>` representing presence
|
|
47
|
+
* (as opposed to `none`, which represents absence).
|
|
48
|
+
*
|
|
49
|
+
* @typeParam T - Type of the wrapped value.
|
|
50
|
+
* @param some - The value to store in the option.
|
|
51
|
+
* @returns An {@link Option} containing the provided value.
|
|
52
|
+
*/
|
|
53
|
+
function some(some) {
|
|
54
|
+
return createOption({ some });
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Creates an {@link Option} representing the absence of a value.
|
|
58
|
+
*
|
|
59
|
+
* @typeParam T - The type of the value that would be contained if present.
|
|
60
|
+
* @returns An {@link Option} in the `None` state.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* const value = none<number>();
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
function none() {
|
|
68
|
+
return createOption(NONE);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Creates an {@link Option} from a nullable value.
|
|
72
|
+
*
|
|
73
|
+
* If {@link nullable} is `null` or `undefined`, returns {@link none}.
|
|
74
|
+
* Otherwise, wraps the provided value in {@link some}.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* fromNullable(0).getOrElse(() => 123); // 0
|
|
78
|
+
* fromNullable(null).toNullable(); // null
|
|
79
|
+
*
|
|
80
|
+
* @typeParam T - The non-null value type to wrap.
|
|
81
|
+
* @param nullable - A value that may be `null` or `undefined`.
|
|
82
|
+
* @returns An {@link Option} that is `Some` when the value is present, otherwise `None`.
|
|
83
|
+
*/
|
|
84
|
+
function fromNullable(nullable) {
|
|
85
|
+
return nullable == null ? none() : some(nullable);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Creates an {@link Option} from an {@link Either} by extracting the Right value.
|
|
89
|
+
*
|
|
90
|
+
* If the Either is Right, returns Some containing the Right value.
|
|
91
|
+
* If the Either is Left, returns None (discarding the Left value).
|
|
92
|
+
*
|
|
93
|
+
* @typeParam L - The type of the Left value (error type) that will be discarded
|
|
94
|
+
* @typeParam R - The type of the Right value that will be preserved in the Option
|
|
95
|
+
* @param either - The Either to convert to an Option
|
|
96
|
+
* @returns Some containing the Right value, or None if the Either was Left
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const success = right(42);
|
|
101
|
+
* fromEither(success) // Some(42)
|
|
102
|
+
*
|
|
103
|
+
* const failure = left("error message");
|
|
104
|
+
* fromEither(failure) // None
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
function fromEither(either) {
|
|
108
|
+
return either.match(() => none(), (right) => some(right));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/option/helpers.ts
|
|
113
|
+
/**
|
|
114
|
+
* Combines two Options using a mapping function.
|
|
115
|
+
* Only applies the mapper if both Options contain values.
|
|
116
|
+
*
|
|
117
|
+
* @param optA - First Option to combine
|
|
118
|
+
* @param optB - Second Option to combine
|
|
119
|
+
* @param mapper - Function that combines both values
|
|
120
|
+
* @returns Option containing the combined result, or None if either Option is None
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* const greet = (first, last) => `Hi ${first} ${last}!`
|
|
125
|
+
* map2(some("John"), some("Doe"), greet) // Some("John Doe")
|
|
126
|
+
* map2(some("John"), none<string>(), greet) // None
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
function map2(optA, optB, mapper) {
|
|
130
|
+
return optA.map((a) => (b) => mapper(a, b)).ap(optB);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Combines three Options using a mapping function.
|
|
134
|
+
* Only applies the mapper if all three Options contain values.
|
|
135
|
+
*
|
|
136
|
+
* @param optA - First Option to combine
|
|
137
|
+
* @param optB - Second Option to combine
|
|
138
|
+
* @param optC - Third Option to combine
|
|
139
|
+
* @param mapper - Function that combines all three values
|
|
140
|
+
* @returns Option containing the combined result, or None if any Option is None
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* const toDateString = (d, m, y) => `${d}/${m}/${y}`
|
|
145
|
+
* map3(some(15), some(6), some(2023), toDateString) // Some("15/6/2023")
|
|
146
|
+
* map3(some(15), some(6), none<number>(), toDateString) // None
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
function map3(optA, optB, optC, mapper) {
|
|
150
|
+
return optA.map((a) => (b) => (c) => mapper(a, b, c)).ap(optB).ap(optC);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Converts an array of Options into an Option of array.
|
|
154
|
+
* If all Options in the array contain values, returns Some containing an array of all values.
|
|
155
|
+
* If any Option in the array is None, returns None.
|
|
156
|
+
*
|
|
157
|
+
* @param opts - Array of Options to sequence
|
|
158
|
+
* @returns Some containing array of all values if all Options are Some, otherwise None
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* sequence([some(1), some(2), some(3)]) // Some([1, 2, 3])
|
|
163
|
+
* sequence([some(1), none(), some(3)]) // None
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
function sequence(opts) {
|
|
167
|
+
const out = [];
|
|
168
|
+
for (const opt of opts) {
|
|
169
|
+
const [value] = opt.toArray();
|
|
170
|
+
if (!value) return none();
|
|
171
|
+
out.push(value);
|
|
172
|
+
}
|
|
173
|
+
return some(out);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
//#endregion
|
|
177
|
+
export { NONE, fromEither, fromNullable, map2, map3, none, sequence, some };
|
package/dist/task.d.mts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/task.d.ts
|
|
2
|
+
type TaskV<T> = () => Promise<T>;
|
|
3
|
+
type Task<_T> = {
|
|
4
|
+
map: never;
|
|
5
|
+
ap: never;
|
|
6
|
+
flatMap: never;
|
|
7
|
+
tap: never;
|
|
8
|
+
run: never;
|
|
9
|
+
};
|
|
10
|
+
declare function task<T>(): Task<T>;
|
|
11
|
+
declare function fromPromise<T>(_thunk: () => Promise<T>): void;
|
|
12
|
+
declare function all(): void;
|
|
13
|
+
declare function traverse(): void;
|
|
14
|
+
declare function sequence(): void;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { Task, TaskV, all, fromPromise, sequence, task, traverse };
|
package/dist/task.mjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/task.ts
|
|
2
|
+
function task() {
|
|
3
|
+
throw new Error("Not implemented");
|
|
4
|
+
}
|
|
5
|
+
function fromPromise(_thunk) {
|
|
6
|
+
throw new Error("Not implemented");
|
|
7
|
+
}
|
|
8
|
+
function all() {
|
|
9
|
+
throw new Error("Not implemented");
|
|
10
|
+
}
|
|
11
|
+
function traverse() {
|
|
12
|
+
throw new Error("Not implemented");
|
|
13
|
+
}
|
|
14
|
+
function sequence() {
|
|
15
|
+
throw new Error("Not implemented");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
export { all, fromPromise, sequence, task, traverse };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { l as Either } from "./helpers-BGxhvbJN.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/validation.d.ts
|
|
4
|
+
type ValidationV<_E, _T> = object;
|
|
5
|
+
type Validation<_E, _T> = {
|
|
6
|
+
ap: unknown;
|
|
7
|
+
map: unknown;
|
|
8
|
+
map3: unknown;
|
|
9
|
+
};
|
|
10
|
+
declare function valid<T, E = never>(): Validation<E, T>;
|
|
11
|
+
declare function invalid<E, T = never>(): Validation<E, T>;
|
|
12
|
+
declare function fromEither<E, T>(_either: Either<E, T>): Validation<E, T>;
|
|
13
|
+
declare function traverse(): void;
|
|
14
|
+
declare function sequence(): void;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { Validation, ValidationV, fromEither, invalid, sequence, traverse, valid };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/validation.ts
|
|
2
|
+
function valid() {
|
|
3
|
+
throw new Error("Not implemented");
|
|
4
|
+
}
|
|
5
|
+
function invalid() {
|
|
6
|
+
throw new Error("Not implemented");
|
|
7
|
+
}
|
|
8
|
+
function fromEither(_either) {
|
|
9
|
+
throw new Error("Not implemented");
|
|
10
|
+
}
|
|
11
|
+
function traverse() {
|
|
12
|
+
throw new Error("Not implemented");
|
|
13
|
+
}
|
|
14
|
+
function sequence() {
|
|
15
|
+
throw new Error("Not implemented");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
export { fromEither, invalid, sequence, traverse, valid };
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ok-fp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Essential typed effects for TypeScript.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"typescript",
|
|
7
|
+
"fp",
|
|
8
|
+
"functional-programming",
|
|
9
|
+
"effects",
|
|
10
|
+
"algebraic-data-types",
|
|
11
|
+
"monads",
|
|
12
|
+
"option",
|
|
13
|
+
"either",
|
|
14
|
+
"validation",
|
|
15
|
+
"task",
|
|
16
|
+
"taskEither"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://github.com/pwlmc/okfp#readme",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/pwlmc/okfp/issues"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/pwlmc/okfp.git"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": "pwlmc",
|
|
28
|
+
"type": "module",
|
|
29
|
+
"main": "dist/",
|
|
30
|
+
"exports": {
|
|
31
|
+
"./either": {
|
|
32
|
+
"types": "./dist/either.d.mts",
|
|
33
|
+
"default": "./dist/either.mjs"
|
|
34
|
+
},
|
|
35
|
+
"./option": {
|
|
36
|
+
"types": "./dist/option.mts",
|
|
37
|
+
"default": "./dist/option.mjs"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"lint": "biome check src/",
|
|
45
|
+
"test": "vitest",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"build": "tsdown --format esm --dts",
|
|
48
|
+
"dev": "tsdown --format esm --dts --watch",
|
|
49
|
+
"docs:dev": "vitepress dev docs",
|
|
50
|
+
"docs:build": "vitepress build docs",
|
|
51
|
+
"docs:preview": "vitepress preview docs"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@biomejs/biome": "^2.4.4",
|
|
55
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
56
|
+
"tsdown": "^0.20.3",
|
|
57
|
+
"typescript": "^5.9.3",
|
|
58
|
+
"vitepress": "^1.6.4",
|
|
59
|
+
"vitest": "^4.0.18"
|
|
60
|
+
}
|
|
61
|
+
}
|