npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@othree.io/optional

v3.0.0

Published

Implementation of Optional values

Downloads

520

Readme

@othree.io/optional

A TypeScript implementation of the Optional/Maybe monad for handling nullable values and errors without explicit null checks.

Install

npm install @othree.io/optional

Quick start

import { Optional, Empty, Try, TryAsync } from '@othree.io/optional'

const name = Optional('Alice')
name.get()        // 'Alice'
name.isPresent    // true

const nothing = Empty<string>()
nothing.isEmpty   // true
nothing.orElse('default')  // 'default'

Creating Optionals

Wrapping a value

const maybeUser = Optional(user)        // present if user is not null/undefined
const empty = Optional(undefined)       // empty
const empty = Optional(null)            // empty

Empty with an error

const failed = Optional(undefined, new Error('Not found'))
failed.isEmpty       // true
failed.getError()    // Error('Not found')

Empty shorthand

const empty = Empty<string>()                          // empty, no error
const failed = Empty<string>(new Error('Not found'))   // empty, with error

Wrapping code that might throw

const result = Try(() => JSON.parse(raw))
// present with parsed value, or empty with the caught error

const result = await TryAsync(() => fetch('/api/user').then(r => r.json()))
// same, but for async operations

Extracting values

get

Returns the value or throws. If the Optional carries an error, that error is thrown. Otherwise an EmptyOptionalError is thrown.

Optional('hello').get()     // 'hello'
Empty().get()               // throws EmptyOptionalError
Empty(new Error('!')).get() // throws Error('!')

orElse

Returns the value or the provided fallback. If the Optional is in an error state, the error is thrown instead of returning the fallback.

Optional('hello').orElse('world')   // 'hello'
Empty().orElse('world')             // 'world'

orThrow

Returns the value or throws the provided error. If the Optional already carries an error, that original error is thrown.

Optional('hello').orThrow(new Error('!'))   // 'hello'
Empty().orThrow(new Error('!'))             // throws Error('!')

Transforming values

map / mapAsync

Transforms the value if present. If the callback returns an Optional, it is automatically flattened.

Optional({ name: 'Alice' })
    .map(user => user.name)
    .get()  // 'Alice'

Optional({ name: 'Alice' })
    .map(user => Optional(user.name))
    .get()  // 'Alice' (flattened)

// Empty Optionals pass through untouched
Empty<string>()
    .map(s => s.toUpperCase())
    .isEmpty  // true

// Async version
const upper = await Optional('hello')
    .mapAsync(async s => s.toUpperCase())
// upper.get() === 'HELLO'

orMap / orMapAsync

Transforms the value only when the Optional is empty and not in an error state. Useful for providing computed fallbacks.

Empty<number>()
    .orMap(() => 42)
    .get()  // 42

// Error state is preserved, orMap is skipped
Try(() => { throw new Error('!') })
    .orMap(() => 42)
    .isEmpty  // true, error is preserved

catch / catchAsync

Handles the error when the Optional is empty and in an error state. Allows recovery from errors.

Try(() => { throw new Error('KAPOW!') })
    .catch(e => 'recovered')
    .get()  // 'recovered'

// Async version
const result = await Try(() => { throw new Error('!') })
    .catchAsync(async e => 'recovered')
// result.get() === 'recovered'

Checking state

const opt = Optional('hello')
opt.isPresent  // true
opt.isEmpty    // false

const empty = Empty()
empty.isPresent  // false
empty.isEmpty    // true

Error inspection

const failed = Optional(undefined, new Error('Not found'))
failed.getError()  // Error('Not found')

Optional('hello').getError()  // undefined

Type guard

import { isOptional } from '@othree.io/optional'

isOptional(Optional('hello'))  // true
isOptional('hello')            // false

Automatic flattening

Nested Optionals are flattened both at runtime and at the type level:

const nested = Optional(Optional(Optional('hello')))
nested.get()  // 'hello'

// Type-level: Optional<Optional<string>> resolves to Optional<string>

Chaining

Methods can be chained for expressive pipelines:

const displayName = Try(() => fetchUser())
    .map(user => user.profile)
    .map(profile => profile.displayName)
    .catch(e => 'Anonymous')
    .orElse('Guest')

Three-state model

An Optional can be in one of three states:

| State | isPresent | isEmpty | getError() | Behavior | |---|---|---|---|---| | Present | true | false | undefined | Value is available | | Empty | false | true | undefined | No value, no error | | Error | false | true | Error | No value, has error |

This distinction matters for orElse, orMap, and catch:

  • orElse / orMap activate on empty state, but throw on error state
  • catch activates only on error state
  • map activates only on present state

API reference

| Method | Signature | Description | |---|---|---| | get() | () => T | Extract value or throw | | orElse(value) | (value: T) => T | Value or fallback (throws on error) | | orThrow(error) | (error: Error) => T | Value or throw custom error | | map(fn) | <A>(fn: (v: T) => A \| Optional<A>) => Optional<A> | Transform if present | | mapAsync(fn) | <A>(fn: (v: T) => Promise<A \| Optional<A>>) => Promise<Optional<A>> | Async transform if present | | orMap(fn) | <A>(fn: () => A \| Optional<A>) => Optional<A \| T> | Transform if empty (no error) | | orMapAsync(fn) | <A>(fn: () => Promise<A \| Optional<A>>) => Promise<Optional<A \| T>> | Async transform if empty | | catch(fn) | <A>(fn: (e: Error) => A \| Optional<A>) => Optional<A \| T> | Recover from error | | catchAsync(fn) | <A>(fn: (e: Error) => Promise<A \| Optional<A>>) => Promise<Optional<A \| T>> | Async recover from error | | getError() | () => any | Get the error, if any | | isEmpty | boolean | true if no value | | isPresent | boolean | true if value exists |