Option

Option represents an optional value: every Option is either Some, and contains a value, or None, and it's empty.

It is commonly used to represent the result of a function that may not return a value due to failure or missing data, such as a network request, a file read, or a database query.

Constructor

some

Constructs a Some Option, representing an optional value that exists.

import { Option } from 'funkcia';

//       ┌─── Option<number>
//       â–¼
const option = Option.some(10);
// Output: Some(10)

of

Alias of Option.some

Constructs a Some Option, representing an optional value that exists.

import { Option } from 'funkcia';

//       ┌─── Option<number>
//       â–¼
const option = Option.of(10);
// Output: Some(10)

none

Constructs a None Option, representing an optional value that does not exist.

import { Option } from 'funkcia';

function divide(dividend: number, divisor: number): Option<number> {
  if (divisor === 0) {
    return Option.none();
  }

  return Option.some(dividend / divisor);
}

fromNullable

Constructs an Option from a nullable value.

If the value is null or undefined, it returns an Option.None. Otherwise, it returns an Option.Some containing the value.

import { Option } from 'funkcia';

declare const user: User | null

//       ┌─── Option<User>
//       â–¼
const option = Option.fromNullable(user);

fromFalsy

Constructs an Option from a falsy value.

If the value is falsy, it returns an Option.None. Otherwise, it returns an Option.Some with the value.

import { Option } from 'funkcia';

function getEnv(variable: string): string {
  return process.env[variable] ?? '';
}

//       ┌─── Option<string>
//       â–¼
const option = Option.fromFalsy(getEnv('BASE_URL'));

try

Constructs an Option from a function that may throw.

If the function throws or returns null or undefined, it returns an Option.None. Otherwise, it returns an Option.Some with the value.

import { Option } from 'funkcia';

//     ┌─── Option<URL>
//     â–¼
const url = Option.try(() => new URL('example.com'));
// Output: None

firstSomeOf

Returns the first Option.Some value in the iterable. If all values are Option.None, returns Option.None.

import { Option } from 'funkcia';

interface ContactInformation {
  primary: Option<string>;
  secondary: Option<string>;
  emergency: Option<string>;
}

declare const contact: ContactInformation;

//       ┌─── Option<string>
//       â–¼
const option = Option.firstSomeOf([
  contact.primary,
  contact.secondary,
  contact.emergency,
]);

predicate

Returns a function that asserts that a value passes the test implemented by the provided function, creating an Option.Some narrowing down the value to the provided type predicate if the predicate is fulfilled. If the test fails, returns an Option.None instead.

import { Option } from 'funkcia';

//         ┌─── (shape: Shape) => Option<Circle>
//         â–¼
const ensureCircle = Option.predicate(
  (shape: Shape): shape is Circle => shape.kind === 'circle',
);

declare const input: Shape;

//       ┌─── Option<Circle>
//       â–¼
const option = ensureCircle(input);

//          ┌─── (value: number) => Option<number>
//          â–¼
const ensurePositive = Option.predicate(
  (value: number) => value > 0,
);

//       ┌─── Option<number>
//       â–¼
const option = ensurePositive(input);

fun

Declare a function that always returns an Option.

import { Option } from 'funkcia';

// When defining a normal function allowing typescript to infer
// the return type, sometimes the return type will be
// a union of `Option<T>` and `Option<U>` or `Option<never>`

function hasAcceptedTermsOfService(user: User) {
  if (typeof user.termsOfService !== 'boolean')
    return Option.none();

  return user.termsOfService
    ? Option.some('ACCEPTED' as const)
    : Option.some('DECLINED' as const);
}

//       ┌─── Option<'ACCEPTED'> | Option<'DECLINED'> | Option<never>
//       â–¼
const option = hasAcceptedTermsOfService(user);

// When using the `fun` method, the return type is always `Option<T | U>`

const improvedHasAcceptedTermsOfService = Option.fun(hasAcceptedTermsOfService);

//       ┌─── Option<'ACCEPTED' | 'DECLINED'>
//       â–¼
const option = improvedHasAcceptedTermsOfService(user);

enhance

Converts a function that may throw or return a nullable value to an enhanced function that returns an Option.

import { Option } from 'funkcia';

//         ┌─── (text: string, reviver?: Function) => Option<any>
//         â–¼
const safeJsonParse = Option.enhance(JSON.parse);

//       ┌─── Option<any>
//       â–¼
const profile = safeJsonParse('{ "name": "John Doe" }');
// Output: Some({ name: 'John Doe' })

values

Given an array of Options, returns an array containing only the values inside Some.

import { Option } from 'funkcia';

//       ┌─── number[]
//       â–¼
const output = Option.values([
  Option.some(1),
  Option.none<number>(),
  Option.some(3),
]);
// Output: [1, 3]

is

Asserts that an unknown value is an Option.

import { Option } from 'funkcia';

declare const maybeAnOptionWithUser: unknown;

if (Option.is(maybeAnOptionWithUser)) {
  const user = .filter(isUser);
//        â–²
//        └─── Option
}

Instance methods

map

Applies a callback function to the value of the Option when it is Some, returning a new Option containing the new value.

import { Option } from 'funkcia';

//       ┌─── Option<number>
//       â–¼
const option = Option.of(10).map(number => number * 2);
// Output: Some(20)

andThen

Applies a callback function to the value of the Option when it is Some, and returns the new value. Similar to chain (also known as flatMap), with the difference that the callback must return an Option, not a raw value.

import { Option } from 'funkcia';

declare function readFile(path: string): Option<string>;
declare function parseJsonFile(contents: string): Option<FileContent>;

//       ┌─── Option<FileContent>
//       â–¼
const option = readFile('data.json').andThen(parseJsonFile);

filter

Asserts that the Option value passes the test implemented by the provided function. If the test fails, the value is filtered out of the Option, returning a None instead.

import { Option } from 'funkcia';

declare const input: Shape;

//      ┌─── Option<Circle>
//      â–¼
const circle = Option.of(input).filter(
  (shape): shape is Circle => shape.kind === 'circle',
);

//       ┌─── Option<User>
//       â–¼
const option = Option.of(user).filter((user) => user.age >= 21);

or

Replaces the current Option with the provided fallback Option when it is None.

If the current Option is Some, it returns the current Option.

import { Option } from 'funkcia';

// Output: Some('Paul')
const option = Option.some('Paul')
  .or(() => Option.some('John'));


// Output: Some('John')
const greeting = Option.none()
  .or(() => Option.some('John'));

zip

Combines two Options into a single Option containing a tuple of their values, if both Options are Some variants, otherwise, returns None.

import { Option } from 'funkcia';

const first = Option.some('hello');
const second = Option.some('world');

//       ┌─── Option<[string, string]>
//       â–¼
const strings = first.zip(second);
// Output: Some(['hello', 'world'])

zipWith

Combines two Options into a single Option. The new value is produced by applying the given function to both values, if both Options are Some variants, otherwise, returns None.

import { Option } from 'funkcia';

const first = Option.some('hello');
const second = Option.some('world');

//        ┌─── Option<string>
//        â–¼
const greeting = first.zipWith(second, (a, b) => `${a} ${b}`);
// Output: Some('hello world')

match

Compare the Option against the possible patterns and then execute code based on which pattern matches.

import { Option } from 'funkcia';

declare function readFile(path: string): Option<string>;
declare function parseJsonFile(contents: string): Option<FileContent>;

//         ┌─── string
//         â–¼
const userGreeting = readFile('data.json')
  .andThen(parseJsonFile)
  .match({
    Some(contents) {
      return processFile(contents);
    },
    None() {
      return 'File is invalid JSON';
    },
  });

unwrap

Unwraps the Option value.

import { Option } from 'funkcia';

//     ┌─── User
//     â–¼
const user = Option.some(databaseUser).unwrap();

const team = Option.none().unwrap();
// Output: Uncaught exception: 'called "Option.unwrap()" on a "None" value'

unwrapOr

Unwraps the Option value.

If the Option is None, returns the result of the provided callback.

import { Option } from 'funkcia';

//       ┌─── string
//       â–¼
const baseUrl = Option.some(process.env.BASE_URL)
  .unwrapOr(() => 'http://localhost:3000');
// Output: 'https://funkcia.lukemorales.io'

const apiKey = Option.none()
  .unwrapOr(() => 'sk_test_9FK7CiUnKaU');
// Output: 'sk_test_9FK7CiUnKaU'

unwrapOrNull

Use this method at the edges of the system, when storing values in a database or serializing to JSON.

Unwraps the value of the Option if it is a Some, otherwise returns null.

import { Option } from 'funkcia';

declare const findUserById: (id: string) => Option<User>;

//     ┌─── User | null
//     â–¼
const user = findUserById('user_123').unwrapOrNull();

unwrapOrUndefined

Use this method at the edges of the system, when storing values in a database or serializing to JSON.

Unwraps the value of the Option if it is a Some, otherwise returns undefined.

import { Option } from 'funkcia';

declare const findUserById: (id: string) => Option<User>;

//     ┌─── User | undefined
//     â–¼
const user = findUserById('user_123').unwrapOrUndefined();

expect

Unwraps the Option value.

import { Option } from 'funkcia';

declare function findUserById(id: string): Option<User>;

//     ┌─── User
//     â–¼
const user = findUserById('user_123').expect(
  () => new UserNotFound(userId),
);

const anotherUser = findUserById('invalid_id').expect(
  () => new UserNotFound('invalid_id'),
);
// Output: Uncaught exception: 'User not found: "user_123"'

contains

Verifies if the Option contains a value that passes the test implemented by the provided function.

  • Returns true if the predicate is fullfiled by the wrapped value.

  • If the predicate is not fullfiled or if the Option is None, returns false.

import { Option } from 'funkcia';

//         ┌─── boolean
//         â–¼
const isPositive = Option.some(10).contains(num => num > 0);
// Output: true

toResult

Converts an Option to a Result.

If Option is Some, returns a Result.ok. If Option is None, returns a Result.error with a NoValueError or a custom error.

import { Option } from 'funkcia';

declare function findUserById(id: string): Option<User>;

//          ┌─── Result<User, NoValueError>
//          â–¼
const authorizedUser = findUserById('user_123').toResult();

//          ┌─── Result<User, UserNotFound>
//          â–¼
const authorizedUser = findUserById('user_123')
  .toResult(() => new UserNotFound('user_123'));

toAsyncOption

Converts the Option to an AsyncOption.

import { Option } from 'funkcia';

declare function readFile(path: string): Option<string>;
declare function parseJsonFile(contents: string): Option<FileContent>;

//       ┌─── AsyncOption<FileContent>
//       â–¼
const asyncFile = readFile('data.json')
  .andThen(parseJsonFile)
  .toAsyncOption();
// Output: Promise<Some(FileContent)>

toAsyncResult

Converts the Option to an AsyncResult.

import { Option } from 'funkcia';

declare function readFile(path: string): Option<string>;
declare function parseJsonFile(contents: string): Option<FileContent>;

//       ┌─── AsyncResult<FileContent, NoValueError>
//       â–¼
const asyncFile = readFile('data.json')
  .andThen(parseJsonFile)
  .toAsyncResult();
// Output: Promise<Ok(FileContent)>

//       ┌─── AsyncResult<FileContent, InvalidFile>
//       â–¼
const asyncFile = readFile('data.json')
  .andThen(parseJsonFile)
  .toAsyncResult(() => new InvalidFile('data.json'));
// Output: Promise<Ok(FileContent)>

toArray

Converts an Option to an array.

If Option is Some, returns an array with the value. If Option is None, returns an empty array.

import { Option } from 'funkcia';

//       ┌─── number[]
//       â–¼
const output = Option.some(10).toArray();
// Output: [10]

tap

Calls the function with the Option value, then returns the Option itself.

The return value of the provided function is ignored. This allows "tapping into" a function sequence in a pipe, to perform side effects on intermediate results

import { Option } from 'funkcia';

//       ┌─── Option<number>
//       â–¼
const option = Option.some(10)
  .tap((value) => console.log(value)); // Console output: 10

isSome

Returns true if the Option contains a value.

import { Option } from 'funkcia';

declare function findUserById(id: string): Option<User>;

const user = findUserById('user_123');

if (user.isSome()) {
  return user.unwrap(); // `unwrap` will not throw
}

isNone

Returns true if the Option is empty.

import { Option } from 'funkcia';

declare function findUserByEmail(email: string): Option<User>;

const user = findUserByEmail(data.email);

if (user.isNone()) {
  return await createUser(data);
}

return user.unwrap();

equals

By default, it uses referential equality to compare the values, but you can provide a custom equality function for more complex cases.

Compares the Option with another Option and returns true if they are equal.

import { Option } from 'funkcia';

const option = Option.of(10).equals(Option.some(10));
// Output: true

Last updated

Was this helpful?