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

@alexismora/result-type

v1.0.0

Published

A Rust-inspired Result type for TypeScript that makes error handling explicit and unavoidable

Readme

@alexismora/result-type

A Rust-inspired Result type for TypeScript that makes error handling explicit and unavoidable.

Installation

npm install @alexismora/result-type

Quick Start

import { Result, ok, err } from '@alexismora/result-type'

function divide(a: number, b: number): Result<number, string> {
  if (b === 0) {
    return err("Division by zero")
  }
  return ok(a / b)
}

// Force explicit error handling
const result = divide(10, 2)
const value = result.expect("Division should succeed") // 5

// Safe handling with defaults
const safe = divide(10, 0).unwrapOr(0) // 0

Why implement Result?

Traditional Typescript error handling with try/catch is easy to ignore. The Result type forces you to handle errors explicitly, making your code more reliable and predictable.

Key Features

  • Rust-inspired API - Familiar to Rust developers
  • Type-safe - Full TypeScript support with strict inference
  • Zero dependencies - Lightweight and fast
  • Explicit errors - No more forgotten error handling
  • Functional - Chain operations with map, andThen, etc.
  • Dual packages - ESM and CommonJS support

Usage

Table of Contents

Overview

The Result type represents either success (Ok) or failure (Err). It forces you to handle errors explicitly, preventing the common JavaScript pattern of ignoring errors.

import { Result, ok, err } from '@alexismora/result-type'

type MyResult = Result<number, string>
const success: MyResult = ok(42)
const failure: MyResult = err("something went wrong")

Basic Types

Result<T, E>

A type that is either Ok<T> (success with value of type T) or Err<E> (failure with error of type E).

Ok<T, E>

Represents a successful result containing a value of type T.

Err<T, E>

Represents a failed result containing an error of type E.

Creating Results

ok<T, E>(value: T): Result<T, E>

Creates a successful Result containing the given value.

const result = ok(42)
// Result<number, never>

err<T, E>(error: E): Result<T, E>

Creates a failed Result containing the given error.

const result = err("File not found")
// Result<never, string>

Constructor Usage

You can also use the class constructors directly:

import { Ok, Err } from '@alexismora/result-type'

const success = new Ok(42)
const failure = new Err("error message")

Core Methods

isOk(): boolean

Returns true if the result is Ok. Acts as a type guard in TypeScript.

const result = ok(42)

if (result.isOk()) {
  // TypeScript knows this is Ok<number>
  console.log(result.value) // 42
}

isErr(): boolean

Returns true if the result is Err. Acts as a type guard in TypeScript.

const result = err("failed")

if (result.isErr()) {
  // TypeScript knows this is Err<string>
  console.log(result.error) // "failed"
}

expect(message: string): T

Returns the contained Ok value. If the result is Err, throws an error with your custom message.

Use this when you have a good reason to expect success and want to fail fast with a clear message.

const config = loadConfig().expect("Config file must exist")
// If loadConfig() returns Err, throws: "Config file must exist: [error details]"

unwrap(): T

Returns the contained Ok value. If the result is Err, throws with a default message.

Use sparingly - prefer expect() for better error messages or safe methods like unwrapOr().

const value = ok(42).unwrap() // 42
const value = err("failed").unwrap() // Throws: "Called unwrap on an Err value: failed"

unwrapOr(defaultValue: T): T

Returns the contained Ok value, or the provided default if Err.

Use this when you have a sensible fallback value.

const port = getPort().unwrapOr(3000)
// If getPort() fails, uses 3000 as default

unwrapOrElse(fn: (error: E) => T): T

Returns the contained Ok value, or computes a value from the error using the provided function.

Use this when you need to compute a fallback based on the error.

const value = result.unwrapOrElse((error) => {
  console.error("Operation failed:", error)
  return getDefaultValue()
})

Functional Methods

map<U>(fn: (value: T) => U): Result<U, E>

Transforms the Ok value by applying a function. If Err, returns the error unchanged.

Use for transforming successful values while preserving errors.

const result = ok(5)
  .map(x => x * 2)
  .map(x => x.toString())
// Result<string, never> = Ok("10")

const failed = err("oops").map(x => x * 2)
// Result<number, string> = Err("oops")

mapErr<F>(fn: (error: E) => F): Result<T, F>

Transforms the Err value by applying a function. If Ok, returns the value unchanged.

Use for transforming or enriching error information.

const result = err(404)
  .mapErr(code => `HTTP Error ${code}`)
// Result<never, string> = Err("HTTP Error 404")

const success = ok(42).mapErr(e => `Error: ${e}`)
// Result<number, string> = Ok(42)

andThen<U>(fn: (value: T) => Result<U, E>): Result<U, E>

Chains operations that return Results. Also known as flatMap.

Use for sequential operations that can each fail.

const result = readFile("config.json")
  .andThen(content => parseJSON(content))
  .andThen(config => validateConfig(config))

// If any step fails, the error propagates
// If all succeed, you get the final value

or(other: Result<T, E>): Result<T, E>

Returns this result if Ok, otherwise returns the alternative.

Use for providing fallback operations.

const result = readFromCache()
  .or(readFromDatabase())
  .or(readFromAPI())
// Uses the first successful result

match<U>(patterns: { ok: (value: T) => U, err: (error: E) => U }): U

Pattern matching for Results. Calls one function or the other based on the variant.

Use for handling both cases explicitly and transforming to a common type.

const message = result.match({
  ok: (value) => `Success: ${value}`,
  err: (error) => `Failed: ${error}`
})

const status = apiCall().match({
  ok: (data) => ({ success: true, data }),
  err: (error) => ({ success: false, error })
})

Helper Functions

tryCatch<T, E>(fn: () => T, errorHandler?: (error: unknown) => E): Result<T, E>

Wraps a function that might throw an exception into a Result.

Use to convert exception-based code to Result-based code.

const result = tryCatch(
  () => JSON.parse(jsonString),
  (error) => `Parse error: ${error}`
)

if (result.isOk()) {
  console.log("Parsed:", result.value)
} else {
  console.error("Failed:", result.error)
}

Without error handler, the caught error is used directly:

const result = tryCatch(() => riskyOperation())
// Result<ReturnType, unknown>

tryCatchAsync<T, E>(fn: () => Promise<T>, errorHandler?: (error: unknown) => E): Promise<Result<T, E>>

Async version of tryCatch. Wraps an async function that might throw.

Use to convert promise rejections into Results.

const result = await tryCatchAsync(
  async () => await fetch(url).then(r => r.json()),
  (error) => `Network error: ${error}`
)

result.match({
  ok: (data) => console.log("Data:", data),
  err: (error) => console.error("Error:", error)
})

Examples

Basic Error Handling

function divide(a: number, b: number): Result<number, string> {
  if (b === 0) {
    return err("Division by zero")
  }
  return ok(a / b)
}

const result = divide(10, 2)
const value = result.expect("Division should succeed") // 5

const bad = divide(10, 0)
const safe = bad.unwrapOr(0) // 0

File Operations

function readConfig(): Result<Config, string> {
  return tryCatch(
    () => {
      const content = fs.readFileSync("config.json", "utf8")
      return JSON.parse(content)
    },
    (error) => `Failed to read config: ${error}`
  )
}

const config = readConfig()
  .map(c => ({ ...c, loaded: true }))
  .unwrapOr(getDefaultConfig())

API Calls

async function fetchUser(id: string): Promise<Result<User, ApiError>> {
  return tryCatchAsync(
    async () => {
      const response = await fetch(`/api/users/${id}`)
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`)
      }
      return response.json()
    },
    (error) => ({
      code: "FETCH_ERROR",
      message: String(error)
    })
  )
}

// Usage
const result = await fetchUser("123")

const user = result.match({
  ok: (user) => user,
  err: (error) => {
    console.error("Failed to fetch user:", error)
    return null
  }
})

Chaining Operations

function processUserData(userId: string): Result<ProcessedData, string> {
  return fetchUserFromDB(userId)
    .andThen(user => validateUser(user))
    .andThen(user => enrichUserData(user))
    .andThen(data => processData(data))
    .mapErr(error => `User processing failed: ${error}`)
}

// If any step fails, the chain short-circuits and returns the error
// If all succeed, you get the final ProcessedData

Type-Safe Error Handling

type AppError =
  | { type: "NotFound"; resource: string }
  | { type: "Unauthorized"; reason: string }
  | { type: "Validation"; errors: string[] }

function getResource(id: string): Result<Resource, AppError> {
  if (!isAuthenticated()) {
    return err({ type: "Unauthorized", reason: "Not logged in" })
  }

  const resource = db.find(id)
  if (!resource) {
    return err({ type: "NotFound", resource: id })
  }

  return ok(resource)
}

const result = getResource("123")

result.match({
  ok: (resource) => displayResource(resource),
  err: (error) => {
    switch (error.type) {
      case "NotFound":
        show404(error.resource)
        break
      case "Unauthorized":
        redirectToLogin(error.reason)
        break
      case "Validation":
        showErrors(error.errors)
        break
    }
  }
})

Combining Multiple Results

function loadAppData(): Result<AppData, string> {
  const config = loadConfig()
  const user = loadUser()
  const settings = loadSettings()

  if (config.isErr()) return config
  if (user.isErr()) return user
  if (settings.isErr()) return settings

  return ok({
    config: config.value,
    user: user.value,
    settings: settings.value
  })
}

Graceful Degradation

const data = fetchFromCache()
  .or(fetchFromDatabase())
  .or(fetchFromAPI())
  .unwrapOr(getDefaultData())

// Tries cache first, then database, then API
// Falls back to default data if all fail

Best Practices

  1. Use expect() with descriptive messages when you have a good reason to believe the operation should succeed.

  2. Prefer unwrapOr() over unwrap() when you have a sensible default value.

  3. Use andThen() for chaining operations that can each fail - it's cleaner than nested if/else.

  4. Use match() for exhaustive handling when you need to handle both cases explicitly.

  5. Use typed errors (discriminated unions) for better error handling and IDE support.

  6. Wrap external APIs with tryCatch or tryCatchAsync to convert exceptions to Results.

  7. Don't mix paradigms - once you start using Result, avoid mixing it with try/catch in the same code path.

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Repository

https://github.com/AlexisMora/result-type