An implementation of Scala's Option type, written in javascript, with included type definition files for use in typescript.
npm install --save optionals
Typescript is awesome, and this library includes the type definition files for those who want to include it in their typescript project.
///<reference path="../../node_modules/optionals/lib/option.d.ts"/>
An Option represents a container which holds at most 1 value; it can either hold some value, or it can hold none. As such, there are two constructors for an Option, Some
and None
. Use Some
when you have a definite value, and use None
otherwise.
Example (in typescript):
import {Some, None, Option} from 'optionals';
export function getContactForPhoneNumber(number: String): Option<String> {
if (hasContactForNumber(number)) {
let contact = getContactForNumber(number);
return new Some(contact);
}
else {
return new None();
}
}
Then, from another file, we could say:
import {getContactForPhoneNumber} from 'number-lookup-service';
let contact = getContactForPhoneNumber('867-5309').match({
some: contact => doSomethingWithContact(contact),
none: () => throwErrorOrDoSomethingElse()
});
This removes ambiguous if-statements and null-checks in a type-safe manner - anything returned from either branch of the match
function must be of the same type (when using the provided typescript declaration files), so we know contact
will have the correct type.
The following functions are implementations of the equivalent Scala Option functions, and they are:
match<U>(matcher: { some: (val: T) => U, none: () => U }): U
You saw the match
function used above. One of the amazing things about Scala is its pattern matching, which enables code such as:
val s = Some(123)
s match {
case Some(v) => doSomethingWithTheValue(v)
case None => throwErrorOrSomething()
}
Since the match
keyword isn't part of javascript, this library does its best to emulate the functionality, with:
let s = new Some(123);
s.match({
some: v => doSomethingWithTheValue(v),
none: () => throwErrorOrSomething()
});
While this library doesn't bring full-fledged pattern matching to javascript, it offers the functionality to Options - a functionality without which they would be fairly incomplete.
get(): T
Returns the option's value, undefined if the option is None
.
Example:
let s = new Some(123);
s.get() // 123
let n = new None();
n.get() // undefined
isEmpty(): Boolean
Returns true if the option is None
, false otherwise.
Example:
let s = new Some('Cinnamon');
s.isEmpty() // false
let n = new None();
s.isEmpty() // true
isDefined(): Boolean
Returns true if the option is an instance of Some
, false otherwise.
isDefined
is the negation of isEmpty
, so an example isn't necessary.
contains(value: T): Boolean
Tests whether the option contains a given value as its element.
Example:
let s = new Some([1, 2, 3]);
s.contains([1, 2, 3]); // true
let n = new None();
n.contains('anything'); // false
exists(predicate: (value: T) => Boolean): Boolean
Returns true if this option is nonempty and the predicate returns true when applied to this option's value.
Example:
let s = new Some([1, 2, 3]);
s.exists(val => _.contains(val, 2)) // true
let n = new None();
n.exists(val => _.contains(val, 2)) // false
filter(predicate: (value: T) => Boolean): Option<T>
Returns an option if it is nonempty and applying the predicate to this option's value returns true.
Example (same as above; note the difference in return type):
let s = new Some([1, 2, 3]);
s.filter(val => _.contains(val, 2)) // new Some([1, 2, 3])
let n = new None();
n.filter(val => _.contains(val, 2)) // new None()
flatMap<U>(transformer: (value: T) => Option<U>): Option<U>
Returns the result of applying the transformer to this option's value if this option is nonempty.
Example:
let s = new Some([1, 2, 3]);
s.flatMap(a => new Some(a[0])) // new Some(1)
let n = new None();
n.flatMap(a => new Some(a[0])) // new None()
map<U>(transformer: (value: T) => U): Option<U>
Returns an option containing the result of applying the transformer to this option's value if this option is nonempty.
Example (again, same as above; note the difference in return type of transformer
):
let s = new Some([1, 2, 3]);
s.map(a => a[0]) // new Some(1)
let n = new None();
n.map(a => a[0]) // new None()
In using map
and flatMap
, you really begin to see the power and flexibility of using Options in your code - the eliminate a lot of null-checks and allow you to perform operations on values as if you were certain you had them. Leave it up to the Option to do the resolution for you.
flatten<U>(): Option<U>
Collapse nested options into an option at most 1-layer deep.
Example:
let nested = new Some(new Some(new Some(123)));
nested.flatten() // new Some(123)
getOrElse(other: T): T
Returns the option's value if it exists, other
if it does not.
Example:
let n = new None();
s.getOrElse('default'); // 'default'
orNull(): T
Returns the option's value if it exists, null
if it does not. This is functionally equivalent to saying getOrElse(null)
.
I love Scala almost as much as I love javascript, and I felt that the community was missing out on the functionality provided by the Scala Option API. It's simple and easy to work with, and allows for code to be written in a much more beautiful way.