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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@notimeco/tiny-ts-result

v0.1.7

Published

A minimal result type for typescript.

Readme

tiny-ts-result

A minimal result type for typescript. tiny-ts-result makes it easy to switch from using exception based error handling to using errors as values.

Build and test

Getting started

Install the package.

npm install @notimeco/tiny-ts-result

Start creating Result types.

import type { Result } from "@notimeco/tiny-ts-result";
import { makeResultErr, makeResultOk } from "@notimeco/tiny-ts-result";

// A result function returns either an _ok_ or an _err_.
function getUsernameResult(userId: string): Result<string> {
  if (ifSomethingFails()) {
    return makeResultErr("Something failed");
  }
  return makeResultOk("My username");
}

Very nice, what's it for?

Typescript's structural based type checking makes dealing with complex types a breeze, but the fun typically stops when it's time to do error handling. Every caught error is typed unknown, making it difficult to handle safely and correctly. The Result type enables an errors as values style approach that is both clearer to read and safer to run.

Problems with exception based error handling

Many functions fail and throw exceptions, correct exception handling in typescript can be tedious and error-prone. A javascript function can throw anything, this makes type safe error handling difficult as typescript sets caught values as unknown. A common approach to check for errors is to use javascript's nominal type checking instanceof but this always still leaves the awkward unknown case that needs to be handled in every try catch.

Example: Trying to call getUserOrThrow and handling any error.

/**
 * Load a User from a function that might throw.
 *
 * Handle the error case with an early return.
 * Handle the user case if nothing was thrown.
 */
function exampleWithoutUsingResult(userId: string): void {
  let user: User; // <- Either use `let` or put everything in the `try` scope.
  try {
    user = getUserOrThrow(userId);
  } catch (error: unknown) {
    if (error instanceof Error) {
      handleError(error);
      return;
    }

    // Handle the error again in case it wasn't an Error.
    handleError(new Error("Unknown error", { cause: error })); // <- What is this?
    return;
  }

  handleUser(user);
}

Solutions with errors as values

A result type is an alternative to throwing exceptions where errors are simply values. Instead of throwing a function returns a result, a union type representing either success with the original return type or failure with an error value. tiny-ts-result uses a discriminated union to model a result to take advantage of typescript's strong type narrowing capabilities.

Example: A simpler (non-generic) example of a result union with a discriminator isOk.

type UserResult =
  | {
      isOk: true;
      ok: User;
      err: undefined;
    }
  | {
      isOk: false;
      ok: undefined;
      err: Error;
    };

tiny-ts-result implements a minimal result type that enables clean error handling without needing buy into an entirely different programming paradigm. It tries to solve a few simple problems and then get out of the way.

Consider a function that returns a result type. Destructure the result into ok and err, handle the error in with an early return, and typescript will narrow the type of the ok variable for the remainder of the scope. This keeps the happy case left aligned in the original scope.

Example: Calling getUserResult and handling any error.

/**
 * Load a Result<User, Error>.
 * Handle the error case with an early return.
 * Handle the happy case with a type safe User.
 */
function example(userId: UUID): void {
  const { ok: user, err } = getUserResult(userId);
  if (err) {
    handleError(err);
    return;
  }

  handleUser(user); // <- original scope, not indented
}

Batch processing

Batch processing can make things more difficult again. Often a batch needs to complete as much work as possible and model partial success. Throwing during batch processing unwinds the stack and typically prevents the remainder of the batch from being processed.

Result types avoid the stack unwinding caused by exceptions, allow all the work to complete, and then make it easy to manage.

Example: Easily handling a batch of calls that could fail.

import { groupResults } from "@notimeco/tiny-ts-result";

/**
 * Load a batch of Result<User, Error>.
 * Handle all the errors.
 * Handle all the users.
 */
function example(userIds: UUID[]): void {
  const [users, errs] = groupResults(userIds.map(getUserResult));

  errs.map(handleError);
  users.map(handleUser);
}

Exceptions interoperating with errors as values

Switching from exceptions to errors as values

Most projects will still need to interact with exceptions at some point. A project may be supporting a mix of exceptions, and errors as values. There may also be 3rd party libraries that rely heavily on exceptions for error handling. Dealing with exceptions will still need to happen. Risky functions can be wrapped by try catch and result mapping logic to convert an exception throwing one to a result returning one.

Use wrap to convert any exception throwing function into a result returning one. wrap will run a given task in a try catch block, catch any error and return a result type that is either the expected success type or an error.

Example: Wrapping a function that might throw.

import type { Result } from "@notimeco/tiny-ts-result";
import { wrap } from "@notimeco/tiny-ts-result";

// Catch any errors that may have been thrown and return them in the result type.
const result: Result<User, Error> = wrap(() => getUserOrThrow(userId));

Switching from errors as values back to exceptions

Many applications rely on a top level error handler to translate exceptions back into valid responses. There are also many instances where an exception is actually the best way to solve a problem, catching exceptions with the debugger, or just skipping manual error handling to get a result quicker. Errors as values doesn't have to mean never throw. Result types can be unwrapped to return either the success value or just throw an error.

Use unwrap on any result to either get the success type or throw.

import { unwrap } from "@notimeco/tiny-ts-result";

// Forget about error handling for now; just return the `User` or throw.
const user: User = unwrap(getUserResult());

Examples

Read src/examples for a showcase of all available functionality with some commentary.

What about async?

Everything here is synchronous for now. Promise.allSettled seems to already be just fine. Some async functionality might get added in a later version.