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
npm install @rustable/enumA 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.
- 🔒 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
``bash`
npm install @rustable/enumor
yarn add @rustable/enumor
pnpm add @rustable/enum
Implements Rust-style pattern matching for TypeScript through the Enum base class and variant system.
`typescript
import { Enum } from '@rustable/enum';
// Define an enum with proper type parameter
class MyEnum extends Enum
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'),
});
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.
`typescript
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}),Account suspended: ${reason}
Suspended: (reason) => console.log(),User ${userId} is ${role}
LoggedOut: () => console.log('Please log in'),
});
// or
state2.match({
LoggedIn: (userId, role) => console.log(),
_: () => 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
`typescript
// 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
// Type information is preserved
type UserStateEnum = EnumInstance
`
#### Best Practices for Custom Enums
1. Name Your Enums: Always provide a name parameter to Enums.create for better debugging and error messagesisVariant()
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 methods instead of string-based is()
Represents an optional value that may or may not be present. A type-safe alternative to null/undefined.
`typescript
import { Some, None } from '@rustable/enum';
function divide(a: number, b: number): Option
return b === 0 ? None : Some(a / b);
}
const result = divide(10, 2)
.map((n) => n * 2) // Transform if Some
.unwrapOr(0); // Default if None
`
Represents either success (Ok) or failure (Err). A type-safe way to handle operations that may fail.
`typescript
import { Result } from '@rustable/enum';
// Basic usage
function validateAge(age: number): Result
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
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
// 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);
}
`
`typescript
// 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(),
});
`
`typescript
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);
`
`typescript
const result = Result.Ok(42);
// Pattern matching
result.match({
Ok: (value) => console.log(Success: ${value}),Error: ${error.message}
Err: (error) => console.error(),
});
// Error handling
result.mapErr((err) => new Error(Wrapped: ${err.message})).unwrapOr(0);`
1. Use Option instead of null/undefined for optional valuesResult` for operations that may fail
2. Use
3. Leverage pattern matching for exhaustive case handling
4. Chain methods to transform values safely
5. Always handle both success and failure cases explicitly
- 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
MIT © illuxiza