Socket
Book a DemoInstallSign in
Socket

iron-enum

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

iron-enum

Rust like enums for Typescript

1.6.3
latest
Source
npmnpm
Version published
Weekly downloads
4
Maintainers
1
Weekly downloads
 
Created
Source

Iron Enum

Super‑lightweight Rust‑style tagged unions for TypeScript — fully type‑safe, zero‑dependency, < 1 kB min+gz.

GitHub Repo stars NPM Version JSR Version npm package minimized gzipped size TypeScript License: MIT

IronEnum lets you model expressive enums (a.k.a. tagged unions) in plain TypeScript and gives you ergonomic helpers inspired by Rust’s Option, Result, and try patterns.

▶ Open playground

Features

  • 🦀 Rust-inspired - Familiar Result, Option, and pattern matching
  • 🎯 Type-safe - Full TypeScript support with excellent type inference
  • 🚀 Zero dependencies - Lightweight and fast (~1kb gzipped)
  • 🔧 Ergonomic API - Intuitive constructors and method chaining
  • 🎮 Pattern matching - Exhaustive match and matchAsync methods
  • 🛡️ Error handling - Built-in Try and TryInto utilities

Installation

npm install iron-enum
# or
yarn add iron-enum
# or
pnpm add iron-enum

Quick Start

import { IronEnum } from 'iron-enum';

// Define your enum variants
const Status = IronEnum<{
  Loading: undefined;
  Ready: { finishedAt: Date };
  Error: { message: string; code: number };
}>();

// Create instances
const loading = Status.Loading();
const ready = Status.Ready({ finishedAt: new Date() });
const error = Status.Error({ message: "Network error", code: 500 });

// Pattern match
const message = ready.match({
  Loading: () => "Still working...",
  Ready: ({ finishedAt }) => `Done at ${finishedAt.toLocaleTimeString()}`,
  Error: ({ message }) => `Failed: ${message}`
});

Core Concepts

Creating Enums

IronEnum uses TypeScript's type system to create discriminated unions with zero runtime overhead:

// Simple enum without payloads
const Direction = IronEnum<{
  North: undefined;
  South: undefined;
  East: undefined;
  West: undefined;
}>();

// Enum with different payload types
const UserEvent = IronEnum<{
  Login: { userId: string; timestamp: Date };
  Logout: { userId: string };
  Update: { userId: string; changes: Record<string, any> };
}>();

// Using the enum
const event = UserEvent.Login({ 
  userId: "user123", 
  timestamp: new Date() 
});

Pattern Matching

The match method ensures exhaustive handling of all variants:

const Shape = IronEnum<{
  Circle: { radius: number };
  Rectangle: { width: number; height: number };
  Triangle: { base: number; height: number };
}>();

const shape = Shape.Circle({ radius: 5 });

const area = shape.match({
  Circle: ({ radius }) => Math.PI * radius ** 2,
  Rectangle: ({ width, height }) => width * height,
  Triangle: ({ base, height }) => (base * height) / 2
});

// With fallback using '_'
const description = shape.match({
  Circle: () => "Round shape",
  _: () => "Polygonal shape"  // Catches Rectangle and Triangle
});

Guards and Conditionals

Use if and ifNot for conditional logic:

const Auth = IronEnum<{
  Authenticated: { user: { id: string; name: string } };
  Anonymous: undefined;
}>();

const auth = Auth.Authenticated({ user: { id: "123", name: "Alice" } });

// Simple boolean check
if (auth.if("Authenticated")) {
  console.log("User is logged in");
}

// With callbacks
const userName = auth.if(
  "Authenticated",
  ({ user }) => user.name,
  () => "Guest"
);

// Inverse check
auth.ifNot(
  "Anonymous",
  () => console.log("User is authenticated")
);

Built-in Types

Result<T, E>

Rust-style error handling:

import { Result, Ok, Err } from 'iron-enum';

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);

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

// Convenience methods
console.log(result.isOk());        // true
console.log(result.unwrap());      // 5
console.log(result.unwrap_or(0));  // 5

Option

Nullable value handling:

import { Option, Some, None } from 'iron-enum';

function findUser(id: string): Option<User> {
  const user = database.find(u => u.id === id);
  return user ? Some(user) : None();
}

const userOption = findUser("123");

// Convert to Result
const userResult = userOption.ok_or("User not found");

// Pattern matching
userOption.match({
  Some: (user) => console.log(`Found: ${user.name}`),
  None: () => console.log("User not found")
});

// Convenience methods
console.log(userOption.isSome());           // boolean
console.log(userOption.unwrap_or(null));    // User | null

Try and TryInto

Automatic exception handling:

import { Try, TryInto } from 'iron-enum';

// Wrap a potentially throwing function
const result = Try.sync(() => {
  return JSON.parse('{"valid": "json"}');
});

// Async version
const asyncResult = await Try.async(async () => {
  const response = await fetch('/api/data');
  return response.json();
});

// Transform existing functions
const safeParse = TryInto.sync(JSON.parse);
const safeReadFile = TryInto.async(fs.promises.readFile);

// Use the wrapped functions
const parseResult = safeParse('{"key": "value"}');
parseResult.match({
  Ok: (data) => console.log("Parsed:", data),
  Err: (error) => console.log("Parse failed:", error)
});

Advanced Usage

Async Pattern Matching

const RemoteData = IronEnum<{
  NotAsked: undefined;
  Loading: undefined;
  Success: { data: any };
  Failure: { error: Error };
}>();

const state = RemoteData.Success({ data: { id: 1, name: "Item" } });

const processed = await state.matchAsync({
  NotAsked: async () => null,
  Loading: async () => "Loading...",
  Success: async ({ data }) => {
    // Async processing
    const enhanced = await enhanceData(data);
    return enhanced;
  },
  Failure: async ({ error }) => {
    await logError(error);
    return null;
  }
});

Serialization

Enums can be easily serialized to JSON:

const Status = IronEnum<{
  Active: { since: Date };
  Inactive: { reason: string };
}>();

const status = Status.Active({ since: new Date() });

// Convert to JSON
console.log(status.toJSON()); 
// { Active: { since: "2024-01-01T00:00:00.000Z" } }

// Parse from JSON
const parsed = Status._.parse({ Active: { since: new Date() } });

Type Guards and Narrowing

const Message = IronEnum<{
  Text: { content: string };
  Image: { url: string; alt?: string };
  Video: { url: string; duration: number };
}>();

function processMessage(msg: typeof Message._.typeOf) {
  // The tag property enables type narrowing
  switch (msg.tag) {
    case "Text":
      console.log(msg.payload.content); // TypeScript knows this is string
      break;
    case "Image":
      console.log(msg.payload.url);     // TypeScript knows this is a string
      break;
    case "Video":
      console.log(msg.payload.duration); // TypeScript knows this is number
      break;
  }
}

Performance Optimization

For performance-critical applications, you can pre-define variant keys:

// Pre-allocated version (no Proxy)
const Status = IronEnum<{
  Idle: undefined;
  Running: { pid: number };
  Stopped: { exitCode: number };
}>({ 
  keys: ["Idle", "Running", "Stopped"] // <- provide all keys in an array available at runtime.
});

// This avoids the Proxy overhead for better performance

API Reference

IronEnum Methods

Every enum instance has these methods:

  • tag: The variant name (discriminant)
  • payload: The variant's associated data
  • toJSON(): Convert to plain object
  • key(): Get the variant key
  • if(key, onMatch?, onMismatch?): Conditional execution
  • ifNot(key, onMismatch?, onMatch?): Inverse conditional
  • match(handlers): Exhaustive pattern matching
  • matchAsync(handlers): Async pattern matching

Result Methods

In addition to enum methods:

  • isOk(): Check if Result is Ok
  • isErr(): Check if Result is Err
  • unwrap(): Get value or throw
  • unwrap_or(default): Get value or default
  • unwrap_or_else(fn): Get value or compute default
  • ok(): Convert to Option

Option Methods

In addition to enum methods:

  • isSome(): Check if Option has value
  • isNone(): Check if Option is None
  • unwrap(): Get value or throw
  • unwrap_or(default): Get value or default
  • unwrap_or_else(fn): Get value or compute default
  • ok_or(error): Convert to Result
  • ok_or_else(fn): Convert to Result with computed error

Best Practices

  • Use exhaustive matching - Always handle all variants or use _ fallback
  • Leverage type inference - Let TypeScript infer types from your variants
  • Prefer Option/Result - Use built-in types for common patterns
  • Keep payloads immutable - Treat enum data as read-only
  • Use meaningful variant names - Make your code self-documenting

Examples

State Machine

const State = IronEnum<{
  Idle: undefined;
  Processing: { taskId: string; startedAt: Date };
  Completed: { taskId: string; result: string };
  Failed: { taskId: string; error: Error };
}>();

class TaskProcessor {
  private state = State.Idle();
  
  start(taskId: string) {
    this.state = State.Processing({ taskId, startedAt: new Date() });
  }
  
  complete(result: string) {
    this.state.if("Processing", ({ taskId }) => {
      this.state = State.Completed({ taskId, result });
    });
  }
  
  getStatus(): string {
    return this.state.match({
      Idle: () => "Ready",
      Processing: ({ taskId }) => `Processing ${taskId}...`,
      Completed: ({ taskId }) => `Task ${taskId} completed`,
      Failed: ({ error }) => `Failed: ${error.message}`
    });
  }
}

Form Validation

const ValidationResult = IronEnum<{
  Valid: { value: string };
  Invalid: { errors: string[] };
}>();

function validateEmail(email: string): ValidationResult {
  const errors: string[] = [];
  
  if (!email) errors.push("Email is required");
  if (!email.includes("@")) errors.push("Invalid email format");
  if (email.length > 100) errors.push("Email too long");
  
  return errors.length > 0 
    ? ValidationResult.Invalid({ errors })
    : ValidationResult.Valid({ value: email.toLowerCase() });
}

// Usage
const result = validateEmail("user@example.com");
result.match({
  Valid: ({ value }) => console.log("Email accepted:", value),
  Invalid: ({ errors }) => console.error("Validation failed:", errors)
});

License

MIT © 2025 Scott Lott

Keywords

typescript, enum, tagged union, tagged unions, discriminated union, algebraic data type, adt, sum type, union types, rust enums, rust, pattern matching, option type, result type, functional programming

Made with ❤️ by developers who miss Rust's enums in TypeScript

Keywords

typescript

FAQs

Package last updated on 13 Jul 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.