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

ts-safe

v0.0.5

Published

A functional, type-safe utility library for elegant error handling and asynchronous operations in JavaScript/TypeScript.

Readme

🔗 ts-safe 🔗

English | 한국어

Safe is a functional utility library for JavaScript/TypeScript that simplifies error handling and asynchronous operations. It provides a fluent chainable API that makes your code more readable and maintainable while ensuring strong type safety.

Provides core functionality without the complexity of large libraries like fp-ts or effect.ts. Recognizing that most developers only use a fraction of what big libraries offer, Safe focuses on just the essentials with an intuitive API. All you need are the fundamental tools for type safety, error handling, and async operations.

chain

Features

  • Stable Type Safety: Provides consistent type safety regardless of whether operations are synchronous or asynchronous
  • Chainable API: Enables intuitive method chaining for cleaner code structure
  • Separation of Pure Functions and Side Effects: Clearly distinguishes between methods with side effects and those without
  • Safe Chaining: Allows the chain to continue execution even when errors occur
  • Flexible Error Handling: Control when errors surface through unwrap() or provide fallbacks with orElse()
  • Function Composition: Create reusable function pipelines with the Pipe module

Installation

npm install ts-safe

Basic Usage

import { safe } from 'ts-safe';

const result = safe(10)
  .map((x) => x * 2) // Transform the value (10 -> 20)
  .ifOk(logValue) // Side effect with error propagation
  .ifFail(handleErrors) // Error recovery if needed
  .watch(observeState); // Side effect without error propagation

console.log(result.isOk); // Check if chain contains a success value
console.log(result.unwrap()); // Extract the final value (throws if there was an error)

Starting a Chain

import { safe } from 'ts-safe';

// Start with a value
const s1 = safe(100);

const s2 = safe(() => {
  return 100;
});

const s3 = safe();

s1.unwrap() === s2.unwrap()
s3.unwarp() === undefined

map: Transforming Values

// Example of transforming values in a chain
const result = safe(5)
  .map((x) => x * 2) // 5 -> 10
  .map((x) => x + 3) // 10 -> 13
  .map((x) => `The value is ${x}`); // 13 -> "The value is 13"

console.log(result.isOk); // true
console.log(result.unwrap()); // "The value is 13"

// If there's an error, subsequent map operations are not executed
const errorResult = safe(1)
  .map((x) => {
    throw new Error('Error occurred');
  })
  .map((x) => x * 2); // This transformation is skipped due to the error

console.log(errorResult.isOk); // false

try {
  errorResult.unwrap(); // Throws the error
} catch (e) {
  console.error(e.message); // "Error occurred"
}

const promiseResult =  safe(id)
            .map(async (id)=>fetchData(id));

console.log(promiseResult.isOk); //Promise<boolean>
promiseResult.unwrap() // Promise<Data>

ifOk: Applying Side Effects on Success

// Synchronous ifOk example - note that the return value doesn't change chain value
const syncResult = safe(42)
  .ifOk((value) => {
    console.log(`Processing value: ${value}`);
    return Boolean(value); // This return value doesn't affect the chain's value
  })
  .unwrap(); // Still returns 42

// Asynchronous ifOk example - returning a Promise makes chain async
const asyncResult = safe('data')
  .ifOk(async (data) => {
  // The chain becomes asynchronous when a Promise is returned
  await saveToDatabase(data);
  console.log('Data saved successfully');
});

await asyncResult.isOk; // Promise<true>
const result = await asyncResult.unwrap(); // "data"

// ifOk with error propagation example
const errorResult = safe('data')
.ifOk((data) => {
  throw new Error('Save Error'); // This error propagates through the chain
});
console.log(errorResult.isOk); // false
// errorResult.unwrap();          // Would throw 'Save Error'

watch: Observing Values

// watch observes values and errors without affecting the chain
const result = safe(42)
  .watch((result) => {

    const {isOk,error,value} = result as SafeResult;

    if (result.isOk) {
      console.log(`Current value: ${result.value}`); // "Current value: 42"
    } else {
      console.error(`Error occurred: ${result.error.message}`);
    }
    // Errors thrown here don't affect the chain
    throw new Error('This error is ignored!');
  })
  .map((x) => x * 2); // 42 -> 84

console.log(result.isOk); // true
console.log(result.unwrap()); // 84

// Returning a Promise in watch doesn't affect the chain's synchronicity
const syncChain = safe(10)
  .watch(async (result) => {
    if (result.isOk) {
      await someAsyncOperation();
      console.log('Async operation completed');
    }
  })
  .map((x) => x + 5); // Chain remains synchronous

console.log(syncChain.unwrap()); // 15 (synchronous return)

ifFail: Recovering from Errors

// Error recovery example
const result = safe(() => {
  throw new Error('Initial error');
})
  .map((x) => x + 10) // Not executed due to the error
  .ifFail((error) => {
    console.log(`Error recovery: ${error.message}`);
    return 42; // Provide a fallback value
  })
  .map((x) => x * 2); // Applied to the recovered value (42)

console.log(result.unwrap()); // 84

// fallback
const fallBackResult = safe(() => {
  throw new Error('Async recovery needed');
});

console.log(fallBackResult.isOk); // false
console.log(fallBackResult.orElse(100)); // 100 no error

API Reference

Core Functions

safe<T>(value: T): Safe<T>

safe<T>(fn: () => T): Safe<T>

safe(): Safe<undefined>

Wraps a value or function in a Safe. If a function is provided, it executes the function and stores the result in the Safe.

Main Methods

map<U>(transform: (value: T ) => U): Safe<T extends Promise<any> ? Promise<U> : U>

Transforms the value in the chain. Changes the value type from T to U.

watch(consumer: (result: SafeResult<T) => any): Safe<T>

Observes the current state of the chain without affecting it. Receives a result object containing either a value or an error.

ifOk<U>(effectFn: (value: T ) => U): Safe<U extends Promise<any> ? Promise<T> : T>

Conditionally applies a side effect ONLY when the Safe contains a success value (isOk = true). Completely skipped if the chain is in an error state. If it returns a Promise, the chain becomes asynchronous. Any errors thrown inside propagate to the chain.

ifFail<U>(handler: (error: Error) => U): Safe<T|U>

Conditionally executes the handler ONLY when the Safe contains an error (isOk = false). Provides a fallback or recovery value to continue the chain. Completely skipped if the chain is in a success state. If it returns a Promise, the chain becomes asynchronous.

isOk: Promise<boolean> | boolean

Property that indicates whether the chain contains a success value. Returns a Promise for asynchronous chains.

unwrap(): T

Extracts the final value from the chain. Throws an exception if the chain contains an error.

orElse<U>(fallbackValue: U): T | U

Extracts the value from the chain or returns the provided fallback value if there's an error. Unlike unwrap(), this method never throws an exception.

Synchronous/Asynchronous Processing and Error Propagation

Safe intelligently handles synchronous and asynchronous operations according to these rules:

  1. Methods without side effects (watch)

    • Returning a Promise does not affect the chain's synchronicity
    • Errors that occur inside do not propagate to the chain
    • Access to both values and errors through the Result object
  2. Methods with side effects (ifOk, ifFail)

    • Returning a Promise makes the chain asynchronous
    • Errors that occur inside propagate to the chain
  3. Value transformation methods (map)

    • Returning a Promise makes the chain asynchronous
    • Errors that occur inside propagate to the chain

Additional Modules

Safe comes with several powerful modules that extend its functionality:

🛠️ util: Helpful Utilities

Full Documentation

Common validation and observer and retry patterns:

import { safe,errorIfNull, errorIfEmpty, watchOk, watchError,retry } from 'ts-safe';

safe(userData)
  .ifOk(errorIfNull('User data is required')) // valid
  .map((user) => user.email)
  .ifOk(errorIfEmpty('Email cannot be empty'))
  .ifOk(retry(sendMail)) // retry 
  .watch(watchOk(value => console.log(value))) // obserbe
  .watch(watchError(error => console.error(error)));

📦 pipe: Function Composition Made Easy

Full Documentation

Create reusable function pipelines that process data sequentially:

import { safe } from 'ts-safe';

const processNumber = safe.pipe(
  (num: number) => num * 2,
  (num: number) => num + 10,
  (num: number) => `Result: ${num}`
);

console.log(processNumber(5).unwrap()); // "Result: 20"

License

MIT