MaybeTyped
MaybeTyped is a well-typed Maybe (optional) monad written in typescript.
npm install maybetyped
Usage Examples
; ; // without Maybe ;;if username !== undefined
Api
map
Map gives access to the contained value.
Imagine an array, Array<string>
, as a container for strings, the map
function applies a function to each element if the container is not empty and gives back a new container.
For instance:
;;
some'thing'.mapconsole.logv // prints "thing" none.mapconsole.logv // does not print
tap
Like map
, it calls a function with the current value, if there is one, but ignores any return value and the result is always the original Maybe. Intended for running side-effects, without changing the value.
some1 // If this was `.map`, then the result of this call would be None .tapconsole.log`Original value is ` .mapx + 1 .tapconsole.log`New value is `
flatMap
FlatMap also accesses the contained value, but it expects that its "mapper" function returns a container of the same type. Imagine the conceptually equivalent array container:
;;console.lognow; // => [1, 2, 3, 4, 5, 6]
; ; // Maybe<3> ; // Maybe<undefined>
or
Similar to the or
logical operator.
Tries to get the value (true) of the first maybe; if it is empty (false), tries to get the value (true) of the second maybe.
If both are empty (false), returns an empty (false) maybe.
;; ; // Maybe<22>
orElse
Similar to or
, except the second value is not allowed to be empty.
orElse
must return an instance of the contained value, even if the maybe is empty.
This is useful for supplying default values:
;;
;; ; // 'hi';
expect
expect
forcefully gets the value out of the Maybe
container, or throws an error if there is no value.
This is useful whenever you know the value must be defined at this point, and you want to get out of the Maybe
chain.
For instance:
// The string must be created by one of these 3, we just don't know which ;
; ;// throws an error with the given message if value is null// otherwise returns value
caseOf
caseOf
is a pattern matcher for the Maybe
.
This is useful when you want to execute different logic dependent on whether the container is empty.
For instance:
maybeData.caseOf;
getData.caseOf;// executes the "some" function if not null// executes the "none" function if null
asNullable
asNullable
provides an "out" for escaping the Maybe
container.
This is particularly useful at the boundaries of your API.
Often the internals of a library use Maybe
to clean up code, but would like their external contracts to not be forced to use Maybe
s, but instead "vanilla" JS.
For instance:
;; assertnullable === value;
join
join
takes a "joiner" function and another Maybe
instance and combines them.
If either of the Maybe
s are empty, then the joiner function is not called.
;; ;
MaybeT
.mapuser.birthday .mapnew Datedate .orElseDate.now; // <- this is probably a bad design choice :P .mapapiUserSearch .mapmaybeUser .mapuser.birthday .mapnew Datedate .orElseDate.now .asNullable; ;
Api
maybeT
maybeT
is the constructor for a maybe transform.
Anything with a map
function can be transformed into a maybeT
.
Due to the commonality of the use case, support for thenables
is also added, though be warned that then
matches flatMap
semantics, not map
semantics.
; // MaybeT<Array<number>>; // MaybeT<Promise<string>>
map
.mapparseIntx; // MaybeT<Array<number>>
caseOf
.caseOf; // MaybeT<Array<number>> => MaybeT<[2, 3, 4, 5]>
orElse
.orElse3; // MaybeT<Array<number>> => MaybeT<[1, 2, 3, 4]>
asNullable
.asNullable; // Array<number> => [1, 2, null, 4]
asType
Because typescript does not have support for higher-kinded-types (HKT), we lose track of which monad-like HKT we are dealing with (Array
or Promise
or other).
This means that after most operations the type will become MaybeT<MonadLike<*>>
.
To cope with this, we provide an asType
method that will allow us to properly "remember" what type of monad we were originally dealing with.
A little type safety will be lost here, as you could lie and say this is an Array
instead of a Promise
, but the constructor that is passed in to this method will confirm the type at runtime.
This method also asks for the contained type, but because we haven't forgotten that, we will be able to check that.
Programmatic examples below should help make this more clear.
.asTypePromise; // Promise<string> => this is correct .asTypeArray; // Array<string> => this will throw a runtime err, but not a compile err .asTypePromise; // any => this will throw a compile err, but not runtime .asTypeArray; // any => this will throw a compile err and runtime