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

@rustable/enum

v0.4.10

Published

Rust-inspired pattern matching and type-safe error handling for TypeScript. Includes Option<T> for null-safety and Result<T, E> for error handling, with comprehensive pattern matching support

Downloads

152

Readme

@rustable/enum

A TypeScript implementation of Rust-style pattern matching, Option, and Result types. This package provides type-safe alternatives to null/undefined and error handling patterns in TypeScript.

✨ Features

  • 🔒 Type-safe Enums: Full TypeScript support with proper type inference
  • 🎭 Pattern Matching: Exhaustive pattern matching with compile-time checks
  • 🔄 Option Type: Safe handling of optional values without null/undefined
  • Result Type: Elegant error handling with Ok/Err variants
  • 🎯 Let Pattern: Type-safe conditional execution with proper inference
  • 🔗 Method Chaining: Rich set of combinators for value transformation
  • 🛡️ Null Safety: Complete elimination of null/undefined related bugs
  • 🏭 Factory API: Easy creation of custom enums with type checking
  • 🔄 Async Support: First-class support for async operations with Result type
  • 📦 Zero Dependencies: No external runtime dependencies

📦 Installation

npm install @rustable/enum
# or
yarn add @rustable/enum
# or
pnpm add @rustable/enum

📚 Core Components

Pattern Matching (enum.ts)

Implements Rust-style pattern matching for TypeScript through the Enum base class and variant system.

import { Enum } from '@rustable/enum';

// Define an enum with proper type parameter
class MyEnum extends Enum<typeof MyEnum> {
  static Variant1(value: string) {
    return new MyEnum('Variant1', value);
  }
  static Variant2(value: number) {
    return new MyEnum('Variant2', value);
  }
}

const value = MyEnum.Variant1('test');

// Type-safe pattern matching requires all variants to be handled
value.match({
  Variant1: (str) => console.log(str),
  Variant2: (num) => console.log(num),
});

// Conditional execution with let pattern matching
value.let('Variant1', {
  if: (str) => console.log(`Found Variant1: ${str}`),
  else: () => console.log('Not Variant1'),
});

Custom Enums (Enums.create)

Create type-safe enums with custom variants and parameters using Enums.create. This provides a more concise and type-safe way to define enums compared to class extension.

import { Enums } from '@rustable/enum';

// Define enum with different variant signatures
const UserState = Enums.create('UserState', {
  LoggedOut: () => {},
  LoggedIn: (userId: string, role: string) => {},
  Suspended: (reason: string) => {},
});

// Or
const UserState = Enums.create({
  LoggedOut: () => {},
  LoggedIn: (userId: string, role: string) => {},
  Suspended: (reason: string) => {},
});

// Create instances with type checking
const state1 = UserState.LoggedOut();
const state2 = UserState.LoggedIn('user123', 'admin');
const state3 = UserState.Suspended('violation');

// Type-safe pattern matching (must handle all variants)
state2.match({
  LoggedIn: (userId, role) => console.log(`User ${userId} is ${role}`),
  Suspended: (reason) => console.log(`Account suspended: ${reason}`),
  LoggedOut: () => console.log('Please log in'),
});
// or
state2.match({
  LoggedIn: (userId, role) => console.log(`User ${userId} is ${role}`),
  _: () => console.log('Not logged in'),
});

// Conditional execution with let pattern matching
state2.letLoggedIn({
  if: (userId, role) => console.log(`User ${userId} is ${role}`),
  else: () => console.log('Not logged in'),
});

// Type-safe variant checking
if (state2.isLoggedIn()) {
  // TypeScript knows this is a LoggedIn variant
  console.log(state2.unwrapTuple()); // ['user123', 'admin']
}

// Clone support
const clonedState = state2.clone();

Type Definitions

// Define variant signatures
type UserStateVariants = {
  LoggedOut: () => void;
  LoggedIn: (userId: string, role: string) => void;
  Suspended: (reason: string) => void;
};

const userStateVariants: UserStateVariants = {
  LoggedOut: () => {},
  LoggedIn: (userId: string, role: string) => {},
  Suspended: (reason: string) => {},
};

// Create enum with type information
const UserState = Enums.create<UserStateVariants>('UserState', userStateVariants);

// Type information is preserved
type UserStateEnum = EnumInstance<UserStateVariants>;

Best Practices for Custom Enums

  1. Name Your Enums: Always provide a name parameter to Enums.create for better debugging and error messages
  2. Type Parameters: Use explicit type parameters when complex type inference is needed
  3. Variant Arguments: Use underscore prefix for unused parameters to show intent
  4. Pattern Matching: Always handle all variants or provide a default case
  5. Type-safe Checks: Use generated isVariant() methods instead of string-based is()

Option Type (option.ts)

Represents an optional value that may or may not be present. A type-safe alternative to null/undefined.

import { Some, None } from '@rustable/enum';

function divide(a: number, b: number): Option<number> {
  return b === 0 ? None : Some(a / b);
}

const result = divide(10, 2)
  .map((n) => n * 2) // Transform if Some
  .unwrapOr(0); // Default if None

Result Type (result.ts)

Represents either success (Ok) or failure (Err). A type-safe way to handle operations that may fail.

import { Result } from '@rustable/enum';

// Basic usage
function validateAge(age: number): Result<number, Error> {
  return age >= 0 && age <= 120 ? Result.Ok(age) : Result.Err(new Error('Invalid age'));
}

const result = validateAge(25)
  .map((age) => age + 1) // Transform if Ok
  .unwrapOr(0); // Default if Err

// Async operation handling
const asyncResult = await Result.fromAsync<string, Error>(
  fetch('https://api.example.com/data').then((res) => res.text()),
);

if (asyncResult.isOk()) {
  console.log('Got data:', asyncResult.unwrap());
} else {
  console.error('Failed:', asyncResult.unwrapErr().message);
}

// Function error wrapping
const parseJSON = Result.fromFn<any, Error, [string]>(JSON.parse);

// Success case
const parseResult = parseJSON('{"key": "value"}');
if (parseResult.isOk()) {
  console.log('Parsed:', parseResult.unwrap());
}

// Error case
const errorResult = parseJSON('invalid json');
if (errorResult.isErr()) {
  console.error('Parse failed:', errorResult.unwrapErr().message);
}

📖 Usage

Pattern Matching

// All variants must be handled
enum.match({
  Variant1: (value) => handleVariant1(value),
  Variant2: (value) => handleVariant2(value),
});

// Conditional execution with let pattern
enum.letVariant1({
  if: (value) => handleVariant1(value),
  else: () => handleOtherCases(),
});

Option Type

const opt = Some(5);

// Pattern matching
opt.match({
  Some: (value) => console.log(value),
  None: () => console.log('No value'),
});

// Method chaining
opt
  .map((n) => n * 2)
  .filter((n) => n > 5)
  .unwrapOr(0);

Result Type

const result = Result.Ok(42);

// Pattern matching
result.match({
  Ok: (value) => console.log(`Success: ${value}`),
  Err: (error) => console.error(`Error: ${error.message}`),
});

// Error handling
result.mapErr((err) => new Error(`Wrapped: ${err.message}`)).unwrapOr(0);

📝 Best Practices

  1. Use Option instead of null/undefined for optional values
  2. Use Result for operations that may fail
  3. Leverage pattern matching for exhaustive case handling
  4. Chain methods to transform values safely
  5. Always handle both success and failure cases explicitly

📝 Notes

  • All types are immutable and side-effect free
  • Pattern matching is exhaustive by default
  • Methods follow Rust's naming conventions
  • Full TypeScript type inference support

📄 License

MIT © illuxiza