Optionals are for when a value may or may not be nullish:

// So instead of this signature:
type function = (...) => string | null;

// You can have this:
type function = (...) => Optional<string>;

The difference with this is that the user is forced to check wether or not the value exists before they can access it. This prevents people from just using the value as if it is not nullish.

Control Flows

There are several ways to create control flows for this.

Match Arms

One is match arms:

import { Optional } from '...';

let optional: Optional<T>;

optional.match({
  [Optional.NONE]: () => console.log('Nothing here'),
  [Optional.SOME]: (value) => console.log(value),
});

You can even return match statements!

import { Optional } from '...';

function multiply(optional: Optional<number>): Optional<number> {
  return optional.match({
    [Optional.NONE]: () => Optional.none(),
    [Optional.SOME]: (value) => Optional.some(value * 2),
  });
}

Is Variant Functions

There is also the isSome() and isNone() functions:

import { Optional } from '...';

let optional: Optional<T>;

if (optional.isSome()) {
  console.log(optional.value),
} 

else if (optional.isNone()) {
  console.log('Nothing here'),
}

else {
  // unreachable statement
}

Instance Checks

Lastly there is instance matching:

import { Optional } from '...';

let optional: Optional<T>;

if (optional instanceof Optional.Some) {
  console.log(optional.value),
} 

if (optional instanceof Optional.None) {
  console.log('Nothing here'),
}

if (optional instanceof Optional.Class) {
  // check if some or none
}

Creating

There are several ways to create instances of Optionals. Even if you don't control the functions signature for example.

Exec

The Optional.exec function wraps execution of a function and always returns an Optional:

import { Optional } from '...';
import myFunction from '...';

let optional = Optional.exec(myFunction, ...);

Where it takes the arguments that function would take, fully typed:

import { Optional } from '...';

function divide(a: number, b: number): number | null;

let optional = Optional.exec(divide, 1, 2); // Okay!
let optional = Optional.exec(divide, 1, '3'); // Error: string is not a number
let optional = Optional.exec(divide, 1); // Error: missing argument
let optional = Optional.exec(divide, 1, 2, 3); // Error: too many arguments

Some and None functions

You can also create Optionals when you do control the signature:

import { Optional } from '...';

function divide(a: number, b: number): Optional<number> {
  if (b === 0) return Optional.none();
  else return Optional.some(a / b);
}

From function

You can also let prevent regular if/else patterns using from:

function maybe(a: number | null): Optional<number> {
  return Optional.from(a);
}

The Optional class will figure out of the value is nullish or not.