ts-data-forge 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +534 -0
- package/package.json +101 -0
- package/src/array/array-utils-creation.test.mts +443 -0
- package/src/array/array-utils-modification.test.mts +197 -0
- package/src/array/array-utils-overload-type-error.test.mts +149 -0
- package/src/array/array-utils-reducing-value.test.mts +425 -0
- package/src/array/array-utils-search.test.mts +169 -0
- package/src/array/array-utils-set-op.test.mts +335 -0
- package/src/array/array-utils-slice-clamped.test.mts +113 -0
- package/src/array/array-utils-slicing.test.mts +316 -0
- package/src/array/array-utils-transformation.test.mts +790 -0
- package/src/array/array-utils-validation.test.mts +492 -0
- package/src/array/array-utils.mts +4000 -0
- package/src/array/array.test.mts +146 -0
- package/src/array/index.mts +2 -0
- package/src/array/tuple-utils.mts +519 -0
- package/src/array/tuple-utils.test.mts +518 -0
- package/src/collections/imap-mapped.mts +801 -0
- package/src/collections/imap-mapped.test.mts +860 -0
- package/src/collections/imap.mts +651 -0
- package/src/collections/imap.test.mts +932 -0
- package/src/collections/index.mts +6 -0
- package/src/collections/iset-mapped.mts +889 -0
- package/src/collections/iset-mapped.test.mts +1187 -0
- package/src/collections/iset.mts +682 -0
- package/src/collections/iset.test.mts +1084 -0
- package/src/collections/queue.mts +390 -0
- package/src/collections/queue.test.mts +282 -0
- package/src/collections/stack.mts +423 -0
- package/src/collections/stack.test.mts +225 -0
- package/src/expect-type.mts +206 -0
- package/src/functional/index.mts +4 -0
- package/src/functional/match.mts +300 -0
- package/src/functional/match.test.mts +177 -0
- package/src/functional/optional.mts +733 -0
- package/src/functional/optional.test.mts +619 -0
- package/src/functional/pipe.mts +212 -0
- package/src/functional/pipe.test.mts +85 -0
- package/src/functional/result.mts +1134 -0
- package/src/functional/result.test.mts +777 -0
- package/src/globals.d.mts +38 -0
- package/src/guard/has-key.mts +119 -0
- package/src/guard/has-key.test.mts +219 -0
- package/src/guard/index.mts +7 -0
- package/src/guard/is-non-empty-string.mts +108 -0
- package/src/guard/is-non-empty-string.test.mts +91 -0
- package/src/guard/is-non-null-object.mts +106 -0
- package/src/guard/is-non-null-object.test.mts +90 -0
- package/src/guard/is-primitive.mts +165 -0
- package/src/guard/is-primitive.test.mts +102 -0
- package/src/guard/is-record.mts +153 -0
- package/src/guard/is-record.test.mts +112 -0
- package/src/guard/is-type.mts +450 -0
- package/src/guard/is-type.test.mts +496 -0
- package/src/guard/key-is-in.mts +163 -0
- package/src/guard/key-is-in.test.mts +19 -0
- package/src/index.mts +10 -0
- package/src/iterator/index.mts +1 -0
- package/src/iterator/range.mts +120 -0
- package/src/iterator/range.test.mts +33 -0
- package/src/json/index.mts +1 -0
- package/src/json/json.mts +711 -0
- package/src/json/json.test.mts +628 -0
- package/src/number/branded-types/finite-number.mts +354 -0
- package/src/number/branded-types/finite-number.test.mts +135 -0
- package/src/number/branded-types/index.mts +26 -0
- package/src/number/branded-types/int.mts +278 -0
- package/src/number/branded-types/int.test.mts +140 -0
- package/src/number/branded-types/int16.mts +192 -0
- package/src/number/branded-types/int16.test.mts +170 -0
- package/src/number/branded-types/int32.mts +193 -0
- package/src/number/branded-types/int32.test.mts +170 -0
- package/src/number/branded-types/non-negative-finite-number.mts +223 -0
- package/src/number/branded-types/non-negative-finite-number.test.mts +188 -0
- package/src/number/branded-types/non-negative-int16.mts +187 -0
- package/src/number/branded-types/non-negative-int16.test.mts +201 -0
- package/src/number/branded-types/non-negative-int32.mts +187 -0
- package/src/number/branded-types/non-negative-int32.test.mts +204 -0
- package/src/number/branded-types/non-zero-finite-number.mts +229 -0
- package/src/number/branded-types/non-zero-finite-number.test.mts +198 -0
- package/src/number/branded-types/non-zero-int.mts +167 -0
- package/src/number/branded-types/non-zero-int.test.mts +177 -0
- package/src/number/branded-types/non-zero-int16.mts +196 -0
- package/src/number/branded-types/non-zero-int16.test.mts +195 -0
- package/src/number/branded-types/non-zero-int32.mts +196 -0
- package/src/number/branded-types/non-zero-int32.test.mts +197 -0
- package/src/number/branded-types/non-zero-safe-int.mts +196 -0
- package/src/number/branded-types/non-zero-safe-int.test.mts +232 -0
- package/src/number/branded-types/non-zero-uint16.mts +189 -0
- package/src/number/branded-types/non-zero-uint16.test.mts +199 -0
- package/src/number/branded-types/non-zero-uint32.mts +189 -0
- package/src/number/branded-types/non-zero-uint32.test.mts +199 -0
- package/src/number/branded-types/positive-finite-number.mts +241 -0
- package/src/number/branded-types/positive-finite-number.test.mts +204 -0
- package/src/number/branded-types/positive-int.mts +304 -0
- package/src/number/branded-types/positive-int.test.mts +176 -0
- package/src/number/branded-types/positive-int16.mts +188 -0
- package/src/number/branded-types/positive-int16.test.mts +197 -0
- package/src/number/branded-types/positive-int32.mts +188 -0
- package/src/number/branded-types/positive-int32.test.mts +197 -0
- package/src/number/branded-types/positive-safe-int.mts +187 -0
- package/src/number/branded-types/positive-safe-int.test.mts +210 -0
- package/src/number/branded-types/positive-uint16.mts +188 -0
- package/src/number/branded-types/positive-uint16.test.mts +203 -0
- package/src/number/branded-types/positive-uint32.mts +188 -0
- package/src/number/branded-types/positive-uint32.test.mts +203 -0
- package/src/number/branded-types/safe-int.mts +291 -0
- package/src/number/branded-types/safe-int.test.mts +170 -0
- package/src/number/branded-types/safe-uint.mts +187 -0
- package/src/number/branded-types/safe-uint.test.mts +176 -0
- package/src/number/branded-types/uint.mts +179 -0
- package/src/number/branded-types/uint.test.mts +158 -0
- package/src/number/branded-types/uint16.mts +186 -0
- package/src/number/branded-types/uint16.test.mts +170 -0
- package/src/number/branded-types/uint32.mts +218 -0
- package/src/number/branded-types/uint32.test.mts +170 -0
- package/src/number/enum/index.mts +2 -0
- package/src/number/enum/int8.mts +344 -0
- package/src/number/enum/int8.test.mts +180 -0
- package/src/number/enum/uint8.mts +293 -0
- package/src/number/enum/uint8.test.mts +164 -0
- package/src/number/index.mts +4 -0
- package/src/number/num.mts +604 -0
- package/src/number/num.test.mts +242 -0
- package/src/number/refined-number-utils.mts +566 -0
- package/src/object/index.mts +1 -0
- package/src/object/object.mts +447 -0
- package/src/object/object.test.mts +124 -0
- package/src/others/cast-mutable.mts +113 -0
- package/src/others/cast-readonly.mts +192 -0
- package/src/others/cast-readonly.test.mts +89 -0
- package/src/others/if-then.mts +98 -0
- package/src/others/if-then.test.mts +75 -0
- package/src/others/index.mts +7 -0
- package/src/others/map-nullable.mts +172 -0
- package/src/others/map-nullable.test.mts +297 -0
- package/src/others/memoize-function.mts +196 -0
- package/src/others/memoize-function.test.mts +168 -0
- package/src/others/tuple.mts +160 -0
- package/src/others/tuple.test.mts +11 -0
- package/src/others/unknown-to-string.mts +215 -0
- package/src/others/unknown-to-string.test.mts +114 -0
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-type-assertion */
|
|
2
|
+
import { isRecord } from '../guard/index.mjs';
|
|
3
|
+
import { pipe } from './pipe.mjs';
|
|
4
|
+
|
|
5
|
+
/** @internal Symbol to identify the 'Some' variant of Optional. */
|
|
6
|
+
const SomeTypeSymbol: unique symbol = Symbol('Optional.some');
|
|
7
|
+
|
|
8
|
+
/** @internal Symbol to identify the 'None' variant of Optional. */
|
|
9
|
+
const NoneTypeSymbol: unique symbol = Symbol('Optional.none');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @internal
|
|
13
|
+
* Represents the 'Some' variant of an Optional, containing a value.
|
|
14
|
+
* @template S The type of the contained value.
|
|
15
|
+
*/
|
|
16
|
+
type Some_<S> = Readonly<{
|
|
17
|
+
/** Discriminant property for the 'Some' type. */
|
|
18
|
+
type: typeof SomeTypeSymbol;
|
|
19
|
+
/** The contained value. */
|
|
20
|
+
value: S;
|
|
21
|
+
}>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
* Represents the 'None' variant of an Optional, indicating the absence of a value.
|
|
26
|
+
*/
|
|
27
|
+
type None_ = Readonly<{
|
|
28
|
+
/** Discriminant property for the 'None' type. */
|
|
29
|
+
type: typeof NoneTypeSymbol;
|
|
30
|
+
}>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Represents an optional value that can either be 'Some' (containing a value) or 'None' (empty).
|
|
34
|
+
* @template S The type of the value that might be present.
|
|
35
|
+
*/
|
|
36
|
+
export type Optional<S> = None_ | Some_<S>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Namespace for the {@link Optional} type and related functions.
|
|
40
|
+
* Provides utilities to handle values that might be absent, similar to Option types in other languages.
|
|
41
|
+
*/
|
|
42
|
+
export namespace Optional {
|
|
43
|
+
/**
|
|
44
|
+
* Checks if the given value is an {@link Optional}.
|
|
45
|
+
* @param maybeOptional The value to check.
|
|
46
|
+
* @returns `true` if the value is an {@link Optional}, otherwise `false`.
|
|
47
|
+
*/
|
|
48
|
+
export const isOptional = (
|
|
49
|
+
maybeOptional: unknown,
|
|
50
|
+
): maybeOptional is Optional<unknown> =>
|
|
51
|
+
isRecord(maybeOptional) &&
|
|
52
|
+
Object.hasOwn(maybeOptional, 'type') &&
|
|
53
|
+
((maybeOptional['type'] === SomeTypeSymbol &&
|
|
54
|
+
Object.hasOwn(maybeOptional, 'value')) ||
|
|
55
|
+
maybeOptional['type'] === NoneTypeSymbol);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Represents an {@link Optional} that contains a value.
|
|
59
|
+
* @template S The type of the contained value.
|
|
60
|
+
*/
|
|
61
|
+
export type Some<S> = Some_<S>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Represents an {@link Optional} that does not contain a value (is empty).
|
|
65
|
+
*/
|
|
66
|
+
export type None = None_;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Base type for any {@link Optional}, used for generic constraints.
|
|
70
|
+
* Represents an {@link Optional} with an unknown value type.
|
|
71
|
+
*/
|
|
72
|
+
export type Base = Optional<unknown>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Extracts the value type `S` from an {@link Optional.Some}<S>.
|
|
76
|
+
* If the {@link Optional} is {@link Optional.None}, resolves to `never`.
|
|
77
|
+
* @template O The {@link Optional.Base} type to unwrap.
|
|
78
|
+
*/
|
|
79
|
+
export type Unwrap<O extends Base> = O extends Some<infer S> ? S : never;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Narrows an {@link Optional.Base} type to {@link Optional.Some}<S> if it is a {@link Optional.Some}.
|
|
83
|
+
* If the {@link Optional} is {@link Optional.None}, resolves to `never`.
|
|
84
|
+
* @template O The {@link Optional.Base} type to narrow.
|
|
85
|
+
*/
|
|
86
|
+
export type NarrowToSome<O extends Base> = O extends None ? never : O;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Narrows an {@link Optional.Base} type to {@link Optional.None} if it is a {@link Optional.None}.
|
|
90
|
+
* If the {@link Optional} is {@link Optional.Some}<S>, resolves to `never`.
|
|
91
|
+
* @template O The {@link Optional.Base} type to narrow.
|
|
92
|
+
*/
|
|
93
|
+
export type NarrowToNone<O extends Base> = O extends None ? O : never;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Creates an {@link Optional.Some} containing the given value.
|
|
97
|
+
* @template S The type of the value.
|
|
98
|
+
* @param value The value to wrap in an {@link Optional.Some}.
|
|
99
|
+
* @returns An {@link Optional.Some}<S> containing the value.
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* const someValue = Optional.some(42);
|
|
103
|
+
* const someString = Optional.some("hello");
|
|
104
|
+
* const someObject = Optional.some({ name: "Alice", age: 30 });
|
|
105
|
+
*
|
|
106
|
+
* console.log(Optional.isSome(someValue)); // true
|
|
107
|
+
* console.log(Optional.unwrap(someValue)); // 42
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export const some = <S,>(value: S): Some<S> => ({
|
|
111
|
+
type: SomeTypeSymbol,
|
|
112
|
+
value,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The singleton instance representing {@link Optional.None} (an empty Optional).
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* const emptyValue = Optional.none;
|
|
120
|
+
*
|
|
121
|
+
* console.log(Optional.isNone(emptyValue)); // true
|
|
122
|
+
* console.log(Optional.unwrap(emptyValue)); // undefined
|
|
123
|
+
* console.log(Optional.unwrapOr(emptyValue, "default")); // "default"
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export const none: None = { type: NoneTypeSymbol } as const;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Checks if an {@link Optional} is {@link Optional.Some}.
|
|
130
|
+
* Acts as a type guard.
|
|
131
|
+
* @template O The {@link Optional.Base} type to check.
|
|
132
|
+
* @param optional The {@link Optional} to check.
|
|
133
|
+
* @returns `true` if the {@link Optional} is {@link Optional.Some}, `false` otherwise.
|
|
134
|
+
*/
|
|
135
|
+
export const isSome = <O extends Base>(
|
|
136
|
+
optional: O,
|
|
137
|
+
): optional is NarrowToSome<O> => optional.type === SomeTypeSymbol;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Checks if an {@link Optional} is {@link Optional.None}.
|
|
141
|
+
* Acts as a type guard.
|
|
142
|
+
* @template O The {@link Optional.Base} type to check.
|
|
143
|
+
* @param optional The {@link Optional} to check.
|
|
144
|
+
* @returns `true` if the {@link Optional} is {@link Optional.None}, `false` otherwise.
|
|
145
|
+
*/
|
|
146
|
+
export const isNone = <O extends Base>(
|
|
147
|
+
optional: O,
|
|
148
|
+
): optional is NarrowToNone<O> => optional.type === NoneTypeSymbol;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Unwraps an `Optional`, returning the contained value.
|
|
152
|
+
* Throws an error if the `Optional` is `Optional.None`.
|
|
153
|
+
*
|
|
154
|
+
* This is a safer alternative to direct value access when you know the Optional
|
|
155
|
+
* should contain a value. Use this method when an empty Optional represents
|
|
156
|
+
* a programming error or unexpected condition.
|
|
157
|
+
*
|
|
158
|
+
* @template O The `Optional.Base` type to unwrap.
|
|
159
|
+
* @param optional The `Optional` to unwrap.
|
|
160
|
+
* @returns The contained value if `Optional.Some`.
|
|
161
|
+
* @throws {Error} Error with message "`unwrapThrow()` has failed because it is `None`" if the `Optional` is `Optional.None`.
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* // Safe unwrapping when you know the value exists
|
|
165
|
+
* const config = loadConfig(); // returns Optional<Config>
|
|
166
|
+
* if (Optional.isSome(config)) {
|
|
167
|
+
* const value = Optional.unwrapThrow(config); // Safe to unwrap
|
|
168
|
+
* console.log(value); // Config object
|
|
169
|
+
* }
|
|
170
|
+
*
|
|
171
|
+
* // Unsafe unwrapping - will throw if empty
|
|
172
|
+
* const userInput = Optional.some(42);
|
|
173
|
+
* console.log(Optional.unwrapThrow(userInput)); // 42
|
|
174
|
+
*
|
|
175
|
+
* const empty = Optional.none;
|
|
176
|
+
* try {
|
|
177
|
+
* Optional.unwrapThrow(empty); // throws Error
|
|
178
|
+
* } catch (error) {
|
|
179
|
+
* console.log(error.message); // "`unwrapThrow()` has failed because it is `None`"
|
|
180
|
+
* }
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export const unwrapThrow = <O extends Base>(optional: O): Unwrap<O> => {
|
|
184
|
+
if (isSome(optional)) {
|
|
185
|
+
return optional.value as Unwrap<O>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
throw new Error('`unwrapThrow()` has failed because it is `None`');
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Unwraps an `Optional`, returning the contained value or `undefined` if empty.
|
|
193
|
+
*
|
|
194
|
+
* This function provides a safe way to extract values from Optionals without
|
|
195
|
+
* throwing exceptions. It has overloaded behavior based on the type:
|
|
196
|
+
* - For `Optional.Some<T>`: Always returns `T` (guaranteed by type system)
|
|
197
|
+
* - For general `Optional<T>`: Returns `T | undefined`
|
|
198
|
+
*
|
|
199
|
+
* @template O The `Optional.Base` type to unwrap.
|
|
200
|
+
* @param optional The `Optional` to unwrap.
|
|
201
|
+
* @returns The contained value if `Optional.Some`, otherwise `undefined`.
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* // With Some - guaranteed to return value
|
|
205
|
+
* const some = Optional.some(42);
|
|
206
|
+
* const value = Optional.unwrap(some); // Type: number, Value: 42
|
|
207
|
+
*
|
|
208
|
+
* // With general Optional - may return undefined
|
|
209
|
+
* const maybeValue: Optional<string> = getOptionalString();
|
|
210
|
+
* const result = Optional.unwrap(maybeValue); // Type: string | undefined
|
|
211
|
+
*
|
|
212
|
+
* // Safe pattern for handling both cases
|
|
213
|
+
* const optional = Optional.some("hello");
|
|
214
|
+
* const unwrapped = Optional.unwrap(optional);
|
|
215
|
+
* if (unwrapped !== undefined) {
|
|
216
|
+
* console.log(unwrapped.toUpperCase()); // "HELLO"
|
|
217
|
+
* }
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
export const unwrap: UnwrapFnOverload = (<O extends Base>(
|
|
221
|
+
optional: O,
|
|
222
|
+
): Unwrap<O> | undefined =>
|
|
223
|
+
isNone(optional)
|
|
224
|
+
? undefined
|
|
225
|
+
: ((optional as NarrowToSome<O>).value as Unwrap<O>)) as UnwrapFnOverload;
|
|
226
|
+
|
|
227
|
+
type UnwrapFnOverload = {
|
|
228
|
+
<O extends Some<unknown>>(optional: O): Unwrap<O>;
|
|
229
|
+
|
|
230
|
+
<O extends Base>(optional: O): Unwrap<O> | undefined;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Unwraps an `Optional`, returning the contained value or a default value if it's `Optional.None`.
|
|
235
|
+
*
|
|
236
|
+
* Supports both direct usage and curried form for functional composition.
|
|
237
|
+
* This is often preferred over `unwrap()` when you have a sensible fallback value.
|
|
238
|
+
*
|
|
239
|
+
* @template O The `Optional.Base` type to unwrap.
|
|
240
|
+
* @template D The type of the default value.
|
|
241
|
+
* @param optional The `Optional` to unwrap.
|
|
242
|
+
* @param defaultValue The value to return if `optional` is `Optional.None`.
|
|
243
|
+
* @returns The contained value if `Optional.Some`, otherwise `defaultValue`.
|
|
244
|
+
* @example
|
|
245
|
+
* ```typescript
|
|
246
|
+
* // Direct usage - most common pattern
|
|
247
|
+
* const userAge = Optional.fromNullable(user.age);
|
|
248
|
+
* const displayAge = Optional.unwrapOr(userAge, "Unknown");
|
|
249
|
+
* console.log(`Age: ${displayAge}`); // "Age: 25" or "Age: Unknown"
|
|
250
|
+
*
|
|
251
|
+
* // With different Optional types
|
|
252
|
+
* const some = Optional.some(42);
|
|
253
|
+
* const value1 = Optional.unwrapOr(some, 0);
|
|
254
|
+
* console.log(value1); // 42
|
|
255
|
+
*
|
|
256
|
+
* const none = Optional.none;
|
|
257
|
+
* const value2 = Optional.unwrapOr(none, 0);
|
|
258
|
+
* console.log(value2); // 0
|
|
259
|
+
*
|
|
260
|
+
* // Curried usage for functional composition
|
|
261
|
+
* const unwrapWithDefault = Optional.unwrapOr("default");
|
|
262
|
+
* const result = pipe(Optional.some("hello"))
|
|
263
|
+
* .map(unwrapWithDefault)
|
|
264
|
+
* .value;
|
|
265
|
+
* console.log(result); // "hello"
|
|
266
|
+
*
|
|
267
|
+
* // Chaining with other Optional operations
|
|
268
|
+
* const processValue = (input: string) =>
|
|
269
|
+
* pipe(Optional.fromNullable(input))
|
|
270
|
+
* .map(Optional.map(s => s.toUpperCase()))
|
|
271
|
+
* .map(Optional.unwrapOr("NO INPUT"))
|
|
272
|
+
* .value;
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
export const unwrapOr: UnwrapOrFnOverload = (<O extends Base, D>(
|
|
276
|
+
...args:
|
|
277
|
+
| readonly [optional: O, defaultValue: D]
|
|
278
|
+
| readonly [defaultValue: D]
|
|
279
|
+
): (D | Unwrap<O>) | ((optional: Optional<Unwrap<O>>) => D | Unwrap<O>) => {
|
|
280
|
+
switch (args.length) {
|
|
281
|
+
case 2: {
|
|
282
|
+
const [optional, defaultValue] = args;
|
|
283
|
+
return isNone(optional)
|
|
284
|
+
? defaultValue
|
|
285
|
+
: ((optional as NarrowToSome<O>).value as Unwrap<O>);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
case 1: {
|
|
289
|
+
// Curried version: first argument is default value
|
|
290
|
+
const [defaultValue] = args;
|
|
291
|
+
return (optional: Optional<Unwrap<O>>) =>
|
|
292
|
+
unwrapOr(optional, defaultValue);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}) as UnwrapOrFnOverload;
|
|
296
|
+
|
|
297
|
+
type UnwrapOrFnOverload = {
|
|
298
|
+
<O extends Base, D>(optional: O, defaultValue: D): D | Unwrap<O>;
|
|
299
|
+
|
|
300
|
+
// Curried version
|
|
301
|
+
<S, D>(defaultValue: D): (optional: Optional<S>) => D | S;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Returns the `Optional` if it is `Some`, otherwise returns the alternative.
|
|
306
|
+
*
|
|
307
|
+
* Provides a way to chain Optional operations with fallback values. This is
|
|
308
|
+
* particularly useful for implementing default behavior or cascading lookups.
|
|
309
|
+
* Supports both direct usage and curried form for functional composition.
|
|
310
|
+
*
|
|
311
|
+
* @template O The input `Optional.Base` type.
|
|
312
|
+
* @param optional The `Optional` to check.
|
|
313
|
+
* @param alternative The alternative `Optional` to return if the first is `None`.
|
|
314
|
+
* @returns The first `Optional` if `Some`, otherwise the alternative.
|
|
315
|
+
* @example
|
|
316
|
+
* ```typescript
|
|
317
|
+
* // Direct usage - cascading lookups
|
|
318
|
+
* const primaryConfig = loadPrimaryConfig(); // Optional<Config>
|
|
319
|
+
* const fallbackConfig = loadFallbackConfig(); // Optional<Config>
|
|
320
|
+
* const config = Optional.orElse(primaryConfig, fallbackConfig);
|
|
321
|
+
*
|
|
322
|
+
* // Multiple fallbacks
|
|
323
|
+
* const userPreference = getUserPreference(); // Optional<string>
|
|
324
|
+
* const systemDefault = Optional.some("default-theme");
|
|
325
|
+
* const theme = Optional.orElse(userPreference, systemDefault);
|
|
326
|
+
* console.log(Optional.unwrap(theme)); // User's preference or "default-theme"
|
|
327
|
+
*
|
|
328
|
+
* // Regular usage example
|
|
329
|
+
* const primary = Optional.none;
|
|
330
|
+
* const fallback = Optional.some("default");
|
|
331
|
+
* const result = Optional.orElse(primary, fallback);
|
|
332
|
+
* console.log(Optional.unwrap(result)); // "default"
|
|
333
|
+
*
|
|
334
|
+
* // Curried usage for functional composition
|
|
335
|
+
* const withFallback = Optional.orElse(Optional.some("fallback"));
|
|
336
|
+
* const result2 = pipe(Optional.none)
|
|
337
|
+
* .map(withFallback)
|
|
338
|
+
* .value;
|
|
339
|
+
* console.log(Optional.unwrap(result2)); // "fallback"
|
|
340
|
+
*
|
|
341
|
+
* // Chaining multiple orElse operations
|
|
342
|
+
* const finalResult = pipe(Optional.none)
|
|
343
|
+
* .map(Optional.orElse(Optional.none)) // Still none
|
|
344
|
+
* .map(Optional.orElse(Optional.some("last resort")))
|
|
345
|
+
* .value;
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
export const orElse: OrElseFnOverload = (<
|
|
349
|
+
O extends Base,
|
|
350
|
+
const O2 extends Base,
|
|
351
|
+
>(
|
|
352
|
+
...args:
|
|
353
|
+
| readonly [optional: O, alternative: O2]
|
|
354
|
+
| readonly [alternative: O2]
|
|
355
|
+
):
|
|
356
|
+
| (O | O2)
|
|
357
|
+
| ((optional: Optional<Unwrap<O>>) => Optional<Unwrap<O>> | O2) => {
|
|
358
|
+
switch (args.length) {
|
|
359
|
+
case 2: {
|
|
360
|
+
const [optional, alternative] = args;
|
|
361
|
+
return isNone(optional) ? alternative : optional;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
case 1: {
|
|
365
|
+
const [alternative] = args;
|
|
366
|
+
return (optional: Optional<Unwrap<O>>) => orElse(optional, alternative);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}) as OrElseFnOverload;
|
|
370
|
+
|
|
371
|
+
type OrElseFnOverload = {
|
|
372
|
+
<O extends Base, const O2 extends Base>(
|
|
373
|
+
optional: O,
|
|
374
|
+
alternative: O2,
|
|
375
|
+
): O | O2;
|
|
376
|
+
|
|
377
|
+
// Curried version
|
|
378
|
+
<S, S2>(
|
|
379
|
+
alternative: Optional<S2>,
|
|
380
|
+
): (optional: Optional<S>) => Optional<S> | Optional<S2>;
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Maps an {@link Optional}<S> to {@link Optional}<S2> by applying a function to a contained value.
|
|
385
|
+
* If the {@link Optional} is {@link Optional.None}, it returns {@link Optional.none}.
|
|
386
|
+
* Otherwise, it applies the `mapFn` to the value in `Optional.Some` and returns a new `Optional.Some` with the result.
|
|
387
|
+
* @template O The input `Optional.Base` type.
|
|
388
|
+
* @template S2 The type of the value returned by the mapping function.
|
|
389
|
+
* @param optional The `Optional` to map.
|
|
390
|
+
* @param mapFn The function to apply to the value if it exists.
|
|
391
|
+
* @returns A new `Optional<S2>` resulting from the mapping, or `Optional.None` if the input was `Optional.None`.
|
|
392
|
+
* @example
|
|
393
|
+
* ```typescript
|
|
394
|
+
* const someNumber = Optional.some(5);
|
|
395
|
+
* const mapped = Optional.map(someNumber, x => x * 2);
|
|
396
|
+
* console.log(Optional.unwrap(mapped)); // 10
|
|
397
|
+
*
|
|
398
|
+
* const noneValue = Optional.none;
|
|
399
|
+
* const mappedNone = Optional.map(noneValue, x => x * 2);
|
|
400
|
+
* console.log(Optional.isNone(mappedNone)); // true
|
|
401
|
+
*
|
|
402
|
+
* // Chaining maps
|
|
403
|
+
* const result = Optional.map(
|
|
404
|
+
* Optional.map(Optional.some("hello"), s => s.toUpperCase()),
|
|
405
|
+
* s => s.length
|
|
406
|
+
* );
|
|
407
|
+
* console.log(Optional.unwrap(result)); // 5
|
|
408
|
+
*
|
|
409
|
+
* // Curried version for use with pipe
|
|
410
|
+
* const doubler = Optional.map((x: number) => x * 2);
|
|
411
|
+
* const result2 = pipe(Optional.some(5)).map(doubler).value;
|
|
412
|
+
* console.log(Optional.unwrap(result2)); // 10
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
export const map: MapFnOverload = (<O extends Base, S2>(
|
|
416
|
+
...args:
|
|
417
|
+
| readonly [optional: O, mapFn: (value: Unwrap<O>) => S2]
|
|
418
|
+
| readonly [mapFn: (value: Unwrap<O>) => S2]
|
|
419
|
+
): Optional<S2> | ((optional: O) => Optional<S2>) => {
|
|
420
|
+
switch (args.length) {
|
|
421
|
+
case 2: {
|
|
422
|
+
const [optional, mapFn] = args;
|
|
423
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
424
|
+
return isSome(optional) ? some(mapFn(unwrap(optional)!)) : none;
|
|
425
|
+
}
|
|
426
|
+
case 1: {
|
|
427
|
+
// Curried version: first argument is mapping function
|
|
428
|
+
const [mapFn] = args;
|
|
429
|
+
return (optional: O) => map(optional, mapFn);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}) as MapFnOverload;
|
|
433
|
+
|
|
434
|
+
type MapFnOverload = {
|
|
435
|
+
<O extends Base, S2>(
|
|
436
|
+
optional: O,
|
|
437
|
+
mapFn: (value: Unwrap<O>) => S2,
|
|
438
|
+
): Optional<S2>;
|
|
439
|
+
|
|
440
|
+
// Curried version
|
|
441
|
+
<S, S2>(mapFn: (value: S) => S2): (optional: Optional<S>) => Optional<S2>;
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Applies a function that returns an `Optional` to the value in an `Optional.Some`.
|
|
446
|
+
* If the input is `Optional.None`, returns `Optional.None`.
|
|
447
|
+
* This is the monadic bind operation for `Optional`.
|
|
448
|
+
* @template O The input `Optional.Base` type.
|
|
449
|
+
* @template S2 The value type of the `Optional` returned by the function.
|
|
450
|
+
* @param optional The `Optional` to flat map.
|
|
451
|
+
* @param flatMapFn The function to apply that returns an `Optional`.
|
|
452
|
+
* @returns The result of applying the function, or `Optional.None`.
|
|
453
|
+
* @example
|
|
454
|
+
* ```typescript
|
|
455
|
+
* // Regular usage
|
|
456
|
+
* const parseNumber = (s: string): Optional<number> => {
|
|
457
|
+
* const n = Number(s);
|
|
458
|
+
* return isNaN(n) ? Optional.none : Optional.some(n);
|
|
459
|
+
* };
|
|
460
|
+
*
|
|
461
|
+
* const result = Optional.flatMap(Optional.some("42"), parseNumber);
|
|
462
|
+
* console.log(Optional.unwrap(result)); // 42
|
|
463
|
+
*
|
|
464
|
+
* // Curried usage for pipe composition
|
|
465
|
+
* const parser = Optional.flatMap(parseNumber);
|
|
466
|
+
* const result2 = pipe(Optional.some("42")).map(parser).value;
|
|
467
|
+
* console.log(Optional.unwrap(result2)); // 42
|
|
468
|
+
* ```
|
|
469
|
+
*/
|
|
470
|
+
export const flatMap: FlatMapFnOverload = (<O extends Base, S2>(
|
|
471
|
+
...args:
|
|
472
|
+
| readonly [optional: O, flatMapFn: (value: Unwrap<O>) => Optional<S2>]
|
|
473
|
+
| readonly [flatMapFn: (value: Unwrap<O>) => Optional<S2>]
|
|
474
|
+
): Optional<S2> | ((optional: O) => Optional<S2>) => {
|
|
475
|
+
switch (args.length) {
|
|
476
|
+
case 2: {
|
|
477
|
+
const [optional, flatMapFn] = args;
|
|
478
|
+
return isSome(optional) ? flatMapFn(unwrap(optional)) : none;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
case 1: {
|
|
482
|
+
const [flatMapFn] = args;
|
|
483
|
+
return (optional: O) => flatMap(optional, flatMapFn);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}) as FlatMapFnOverload;
|
|
487
|
+
|
|
488
|
+
type FlatMapFnOverload = {
|
|
489
|
+
<O extends Base, S2>(
|
|
490
|
+
optional: O,
|
|
491
|
+
flatMapFn: (value: Unwrap<O>) => Optional<S2>,
|
|
492
|
+
): Optional<S2>;
|
|
493
|
+
|
|
494
|
+
// Curried version
|
|
495
|
+
<S, S2>(
|
|
496
|
+
flatMapFn: (value: S) => Optional<S2>,
|
|
497
|
+
): (optional: Optional<S>) => Optional<S2>;
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Filters an `Optional` based on a predicate.
|
|
502
|
+
* If the `Optional` is `Some` and the predicate returns true, returns the original `Optional`.
|
|
503
|
+
* Otherwise returns `None`.
|
|
504
|
+
* @template O The input `Optional.Base` type.
|
|
505
|
+
* @param optional The `Optional` to filter.
|
|
506
|
+
* @param predicate The predicate function.
|
|
507
|
+
* @returns The filtered `Optional`.
|
|
508
|
+
* @example
|
|
509
|
+
* ```typescript
|
|
510
|
+
* // Regular usage
|
|
511
|
+
* const someEven = Optional.some(4);
|
|
512
|
+
* const filtered = Optional.filter(someEven, x => x % 2 === 0);
|
|
513
|
+
* console.log(Optional.unwrap(filtered)); // 4
|
|
514
|
+
*
|
|
515
|
+
* // Curried usage for pipe composition
|
|
516
|
+
* const evenFilter = Optional.filter((x: number) => x % 2 === 0);
|
|
517
|
+
* const result = pipe(Optional.some(4)).map(evenFilter).value;
|
|
518
|
+
* console.log(Optional.unwrap(result)); // 4
|
|
519
|
+
* ```
|
|
520
|
+
*/
|
|
521
|
+
export const filter: FilterFnOverload = (<O extends Base>(
|
|
522
|
+
...args:
|
|
523
|
+
| readonly [optional: O, predicate: (value: Unwrap<O>) => boolean]
|
|
524
|
+
| readonly [predicate: (value: Unwrap<O>) => boolean]
|
|
525
|
+
): Optional<Unwrap<O>> | ((optional: O) => Optional<Unwrap<O>>) => {
|
|
526
|
+
switch (args.length) {
|
|
527
|
+
case 2: {
|
|
528
|
+
const [optional, predicate] = args;
|
|
529
|
+
return isSome(optional)
|
|
530
|
+
? pipe(unwrap(optional)).map((value) =>
|
|
531
|
+
predicate(value) ? some(value) : none,
|
|
532
|
+
).value
|
|
533
|
+
: none;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
case 1: {
|
|
537
|
+
// Curried version: first argument is predicate function
|
|
538
|
+
const [predicate] = args;
|
|
539
|
+
return (optional: O) => filter(optional, predicate);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}) as FilterFnOverload;
|
|
543
|
+
|
|
544
|
+
type FilterFnOverload = {
|
|
545
|
+
<O extends Base>(
|
|
546
|
+
optional: O,
|
|
547
|
+
predicate: (value: Unwrap<O>) => boolean,
|
|
548
|
+
): Optional<Unwrap<O>>;
|
|
549
|
+
|
|
550
|
+
// Curried version
|
|
551
|
+
<S>(
|
|
552
|
+
predicate: (value: S) => boolean,
|
|
553
|
+
): (optional: Optional<S>) => Optional<S>;
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Unwraps an `Optional`, returning the contained value or throwing an error with the provided message.
|
|
558
|
+
* @template O The `Optional.Base` type to unwrap.
|
|
559
|
+
* @param optional The `Optional` to unwrap.
|
|
560
|
+
* @param message The error message to throw if the `Optional` is `Optional.None`.
|
|
561
|
+
* @returns The contained value if `Optional.Some`.
|
|
562
|
+
* @throws Error with the provided message if the `Optional` is `Optional.None`.
|
|
563
|
+
* @example
|
|
564
|
+
* ```typescript
|
|
565
|
+
* // Regular usage
|
|
566
|
+
* const some = Optional.some(42);
|
|
567
|
+
* const value = Optional.expectToBe(some, "Value must exist");
|
|
568
|
+
* console.log(value); // 42
|
|
569
|
+
*
|
|
570
|
+
* // Curried usage for pipe composition
|
|
571
|
+
* const getValue = Optional.expectToBe("Value must exist");
|
|
572
|
+
* const value2 = pipe(Optional.some(42)).map(getValue).value;
|
|
573
|
+
* console.log(value2); // 42
|
|
574
|
+
* ```
|
|
575
|
+
*/
|
|
576
|
+
export const expectToBe: ExpectToBeFnOverload = (<O extends Base>(
|
|
577
|
+
...args:
|
|
578
|
+
| readonly [optional: O, message: string]
|
|
579
|
+
| readonly [message: string]
|
|
580
|
+
): Unwrap<O> | ((optional: Optional<Unwrap<O>>) => Unwrap<O>) => {
|
|
581
|
+
switch (args.length) {
|
|
582
|
+
case 2: {
|
|
583
|
+
const [optional, message] = args;
|
|
584
|
+
if (isSome(optional)) {
|
|
585
|
+
return unwrap(optional);
|
|
586
|
+
}
|
|
587
|
+
throw new Error(message);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
case 1: {
|
|
591
|
+
// Curried version: first argument is message
|
|
592
|
+
const [message] = args;
|
|
593
|
+
return (optional: Optional<Unwrap<O>>): Unwrap<O> =>
|
|
594
|
+
expectToBe(optional, message);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}) as ExpectToBeFnOverload;
|
|
598
|
+
|
|
599
|
+
type ExpectToBeFnOverload = {
|
|
600
|
+
<O extends Base>(optional: O, message: string): Unwrap<O>;
|
|
601
|
+
|
|
602
|
+
// Curried version
|
|
603
|
+
<S>(message: string): (optional: Optional<S>) => S;
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Combines two `Optional` values into a single `Optional` containing a tuple.
|
|
608
|
+
* If either `Optional` is `None`, returns `None`.
|
|
609
|
+
* @template A The value type of the first `Optional`.
|
|
610
|
+
* @template B The value type of the second `Optional`.
|
|
611
|
+
* @param optionalA The first `Optional`.
|
|
612
|
+
* @param optionalB The second `Optional`.
|
|
613
|
+
* @returns An `Optional` containing a tuple of both values, or `None`.
|
|
614
|
+
* @example
|
|
615
|
+
* ```typescript
|
|
616
|
+
* const a = Optional.some(1);
|
|
617
|
+
* const b = Optional.some("hello");
|
|
618
|
+
* const zipped = Optional.zip(a, b);
|
|
619
|
+
* console.log(Optional.unwrap(zipped)); // [1, "hello"]
|
|
620
|
+
*
|
|
621
|
+
* const withNone = Optional.zip(a, Optional.none);
|
|
622
|
+
* console.log(Optional.isNone(withNone)); // true
|
|
623
|
+
* ```
|
|
624
|
+
*/
|
|
625
|
+
export const zip = <A, const B>(
|
|
626
|
+
optionalA: Optional<A>,
|
|
627
|
+
optionalB: Optional<B>,
|
|
628
|
+
): Optional<readonly [A, B]> =>
|
|
629
|
+
isSome(optionalA) && isSome(optionalB)
|
|
630
|
+
? some([optionalA.value, optionalB.value] as const)
|
|
631
|
+
: none;
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Converts a nullable value to an `Optional`.
|
|
635
|
+
*
|
|
636
|
+
* This is the primary way to lift nullable values (null or undefined) into
|
|
637
|
+
* the Optional type system. The function treats both `null` and `undefined`
|
|
638
|
+
* as empty values, converting them to `Optional.None`.
|
|
639
|
+
*
|
|
640
|
+
* @template T The type of the nullable value.
|
|
641
|
+
* @param value The nullable value to convert.
|
|
642
|
+
* @returns `Optional.Some<NonNullable<T>>` if the value is not null or undefined, otherwise `Optional.None`.
|
|
643
|
+
* @example
|
|
644
|
+
* ```typescript
|
|
645
|
+
* // Basic nullable conversion
|
|
646
|
+
* const value: string | null = "hello";
|
|
647
|
+
* const optional = Optional.fromNullable(value);
|
|
648
|
+
* console.log(Optional.unwrap(optional)); // "hello"
|
|
649
|
+
* console.log(Optional.isSome(optional)); // true
|
|
650
|
+
*
|
|
651
|
+
* // Handling null values
|
|
652
|
+
* const nullValue: string | null = null;
|
|
653
|
+
* const noneOptional = Optional.fromNullable(nullValue);
|
|
654
|
+
* console.log(Optional.isNone(noneOptional)); // true
|
|
655
|
+
*
|
|
656
|
+
* // Handling undefined values
|
|
657
|
+
* const undefinedValue: number | undefined = undefined;
|
|
658
|
+
* const alsoNone = Optional.fromNullable(undefinedValue);
|
|
659
|
+
* console.log(Optional.isNone(alsoNone)); // true
|
|
660
|
+
*
|
|
661
|
+
* // Common use case with API responses
|
|
662
|
+
* interface User {
|
|
663
|
+
* name: string;
|
|
664
|
+
* email?: string; // Optional field
|
|
665
|
+
* }
|
|
666
|
+
*
|
|
667
|
+
* const user: User = { name: "John" };
|
|
668
|
+
* const email = Optional.fromNullable(user.email);
|
|
669
|
+
* const emailDisplay = Optional.unwrapOr(email, "No email provided");
|
|
670
|
+
* console.log(emailDisplay); // "No email provided"
|
|
671
|
+
*
|
|
672
|
+
* // Chaining with other Optional operations
|
|
673
|
+
* const processNullableInput = (input: string | null) =>
|
|
674
|
+
* Optional.fromNullable(input)
|
|
675
|
+
* .map(Optional.map(s => s.trim()))
|
|
676
|
+
* .map(Optional.filter(s => s.length > 0))
|
|
677
|
+
* .map(Optional.unwrapOr("empty input"));
|
|
678
|
+
* ```
|
|
679
|
+
*/
|
|
680
|
+
export const fromNullable = <T,>(
|
|
681
|
+
value: T | null | undefined,
|
|
682
|
+
): Optional<NonNullable<T>> => (value == null ? none : some(value));
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Converts an `Optional` to a nullable value.
|
|
686
|
+
*
|
|
687
|
+
* This function extracts the value from an Optional, returning `undefined`
|
|
688
|
+
* for empty Optionals. This is useful when interfacing with APIs or systems
|
|
689
|
+
* that expect nullable values rather than Optional types.
|
|
690
|
+
*
|
|
691
|
+
* Note: This returns `undefined` (not `null`) for consistency with JavaScript's
|
|
692
|
+
* undefined semantics and TypeScript's optional properties.
|
|
693
|
+
*
|
|
694
|
+
* @template O The `Optional.Base` type to convert.
|
|
695
|
+
* @param optional The `Optional` to convert.
|
|
696
|
+
* @returns The contained value if `Some`, otherwise `undefined`.
|
|
697
|
+
* @example
|
|
698
|
+
* ```typescript
|
|
699
|
+
* // Basic conversion
|
|
700
|
+
* const some = Optional.some(42);
|
|
701
|
+
* console.log(Optional.toNullable(some)); // 42
|
|
702
|
+
*
|
|
703
|
+
* const none = Optional.none;
|
|
704
|
+
* console.log(Optional.toNullable(none)); // undefined
|
|
705
|
+
*
|
|
706
|
+
* // Interface with nullable APIs
|
|
707
|
+
* interface ApiResponse {
|
|
708
|
+
* data?: string;
|
|
709
|
+
* }
|
|
710
|
+
*
|
|
711
|
+
* const optionalData: Optional<string> = processData();
|
|
712
|
+
* const response: ApiResponse = {
|
|
713
|
+
* data: Optional.toNullable(optionalData)
|
|
714
|
+
* };
|
|
715
|
+
*
|
|
716
|
+
* // Converting back and forth
|
|
717
|
+
* const original: string | undefined = getValue();
|
|
718
|
+
* const optional = Optional.fromNullable(original);
|
|
719
|
+
* const processed = Optional.map(optional, s => s.toUpperCase());
|
|
720
|
+
* const result: string | undefined = Optional.toNullable(processed);
|
|
721
|
+
*
|
|
722
|
+
* // Useful in conditional logic
|
|
723
|
+
* const maybeUser = findUser(id);
|
|
724
|
+
* const userName = Optional.toNullable(maybeUser);
|
|
725
|
+
* if (userName !== undefined) {
|
|
726
|
+
* console.log(`Found user: ${userName}`);
|
|
727
|
+
* }
|
|
728
|
+
* ```
|
|
729
|
+
*/
|
|
730
|
+
export const toNullable = <O extends Base>(
|
|
731
|
+
optional: O,
|
|
732
|
+
): Unwrap<O> | undefined => (isSome(optional) ? unwrap(optional) : undefined);
|
|
733
|
+
}
|