kitsunejs
v0.0.0
Published
Rust-inspired `Result` and `Option` types for TypeScript, enabling type-safe error handling and null safety.
Maintainers
Readme
kitsunejs
Rust-inspired Result and Option types for TypeScript, enabling type-safe error handling and null safety.
Features
- 🦀 Rust-like API: Familiar
Result<T, E>andOption<T>types with methods likemap,andThen,unwrap, etc. - 🔒 Type-safe: Full TypeScript support with proper type inference and narrowing
- 🌳 Tree-shakeable: Fully ESM-ready with optional CJS support
- 📦 Zero dependencies: Lightweight and self-contained
- ⚡ Async-ready: Built-in support for
PromisewithResult.tryAsync
Installation
⚠️ This package is currently under development (v0.0.0) and not yet published to npm.
# npm
npm install kitsunejs
# pnpm
pnpm add kitsunejs
# yarn
yarn add kitsunejsUsage
Result Type
The Result<T, E> type represents either success (Ok<T>) or failure (Err<E>).
Basic Usage
import { Result } from 'kitsunejs';
// Creating Results
const success = Result.ok(42);
const failure = Result.err('Something went wrong');
// Checking variants
if (success.isOk()) {
console.log(success.unwrap()); // 42
}
if (failure.isErr()) {
console.log(failure.unwrapOr(0)); // 0 (default value)
}Error Handling with try/tryAsync
import { Result } from 'kitsunejs';
// Sync: Convert exceptions to Result
const result = Result.try(() => {
return JSON.parse('{"name": "Alice"}');
});
// Async: Handle Promise rejections
type User = {
id: number;
name: string;
}
async function fetchUser(id: number): Promise<Result<User, Error>> {
return Result.tryAsync(async () => {
const response = await fetch(`https://api.example.com/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return (await response.json()) as User;
});
}
async function main() {
const userResult = await fetchUser(1);
if (userResult.isOk()) {
console.log('User:', userResult.unwrap());
} else {
console.error('Failed to fetch user:', userResult.unwrapErr());
// Provide fallback value
const defaultUser = { id: 0, name: 'Unknown' };
console.log('Using default user:', defaultUser);
}
}
main();Chaining Operations
import { Result } from 'kitsunejs';
type User = {
name: string;
age: number;
}
function findUser(id: number): Result<User, string> {
if (id === 1) {
return Result.ok({ name: 'Alice', age: 30 });
}
return Result.err('User not found');
}
// Transform success values with map
const userName = findUser(1)
.map((user) => user.name)
.unwrapOr('Unknown');
console.log(userName); // 'Alice'
// Chain multiple Result-returning operations
function getAge(user: User): Result<number, string> {
if (user.age < 0) {
return Result.err('Invalid age');
}
return Result.ok(user.age);
}
const age = findUser(1)
.andThen((user) => getAge(user))
.unwrapOr(0);Combining Multiple Results
import { Result } from 'kitsunejs';
const results = [
Result.ok(1),
Result.ok(2),
Result.ok(3),
];
// All must be Ok to get Ok<T[]>
const allOk = Result.all(results);
console.log(allOk.unwrap()); // [1, 2, 3]
// Get the first Ok, or Err<E[]> if all fail
const firstOk = Result.any([
Result.err('error1'),
Result.ok(42),
Result.err('error2'),
]);
console.log(firstOk.unwrap()); // 42
// All Err case returns Err<E[]>
const allErr = Result.any([
Result.err('error1'),
Result.err('error2'),
Result.err('error3'),
]);
if (allErr.isErr()) {
console.log(allErr.unwrapOrElse((errors) => errors)); // ['error1', 'error2', 'error3']
}Option Type
The Option<T> type represents an optional value: either Some<T> or None.
Basic Usage
import { Option } from 'kitsunejs';
// Creating Options
const some = Option.some(42);
const none = Option.none();
// Checking variants
if (some.isSome()) {
console.log(some.unwrap()); // 42
}
if (none.isNone()) {
console.log('No value');
}
// Safe handling of null/undefined
function getConfig(key: string): string | null {
// Simulated config lookup
return null;
}
const config = Option.fromNullable(getConfig('api_key'))
.unwrapOr('default-api-key');
console.log(config); // 'default-api-key'Chaining Operations
import { Option } from 'kitsunejs';
// Transform values with map
const doubled = Option.some(10)
.map((n) => n * 2)
.unwrapOr(0);
console.log(doubled); // 20
// Filter values based on predicates
const filtered = Option.some(10)
.filter((n) => n > 15)
.unwrapOr(0);
console.log(filtered); // 0 (filtered out)
// Chain Option-returning operations
function parseNumber(str: string): Option<number> {
const num = Number.parseFloat(str);
if (Number.isNaN(num)) {
return Option.none();
}
return Option.some(num);
}
const result = Option.some('42.5')
.andThen((str) => parseNumber(str))
.map((num) => num * 2)
.unwrapOr(0);
console.log(result); // 85Converting Between Result and Option
import { Result, Option } from 'kitsunejs';
// Option to Result
const option = Option.some(42);
const result = option.toResult('No value provided');
console.log(result.unwrap()); // 42
// Result to Option
const okResult = Result.ok(42);
const optionFromResult = okResult.toOption();
console.log(optionFromResult.unwrap()); // 42
const errResult = Result.err('error');
const noneFromErr = errResult.toOption();
console.log(noneFromErr.isNone()); // trueCombining Multiple Options
import { Option } from 'kitsunejs';
const options = [
Option.some(1),
Option.some(2),
Option.some(3),
];
// All must be Some to get Some<T[]>
const allSome = Option.all(options);
console.log(allSome.unwrap()); // [1, 2, 3]
// Get the first Some, or None if all are None
const firstSome = Option.any([
Option.none(),
Option.some(42),
Option.none(),
]);
console.log(firstSome.unwrap()); // 42Documentation
For more detailed information, please refer to the following documentation:
- API Reference - Complete list of all methods with detailed explanations and examples
- Rust Comparison - Comparison table between Rust's
Result/Optionand kitsune - Recipes - Practical usage patterns and best practices for common scenarios
Contributing
We welcome contributions! Please see our Contributing Guide for details on:
- How to set up your development environment
- Our coding standards and Style Guide
- How to submit pull requests
- Our commit message conventions
Quick API Reference
Below is a quick reference of available methods. For detailed documentation with examples, see API Reference.
Result<T, E> Methods
isOk(),isErr()- Type guardsunwrap(),expect(message)- Extract values (throws on error)unwrapErr()- Extract error value (throws on Ok)unwrapOr(defaultValue),unwrapOrElse(fn)- Safe extraction with fallbackmap(fn),mapErr(fn)- Transform valuesand(other),or(other)- Combine ResultsandThen(fn),orElse(fn)- Chain operationstoOption()- Convert to Option
Result Static Methods
Result.ok(value),Result.err(error)- ConstructorsResult.fromNullable(value, error)- Convert nullable to ResultResult.try(fn),Result.tryAsync(fn)- Exception handlingResult.all(results)- All must beOkto returnOk<T[]>, otherwise returns the firstErrResult.any(results)- Returns the firstOk, orErr<E[]>containing all errors if none succeed
Option Methods
isSome(),isNone()- Type guardsunwrap(),expect(message)- Extract values (throws on None)unwrapOr(defaultValue),unwrapOrElse(fn)- Safe extraction with fallbackmap(fn)- Transform valuesand(other),or(other)- Combine OptionsandThen(fn)- Chain operationsfilter(predicate)- Filter valuestoResult(error)- Convert to Result
Option Static Methods
Option.some(value),Option.none()- ConstructorsOption.fromNullable(value)- Convert nullable to OptionOption.all(options)- All must beSometo returnSome<T[]>, otherwise returnsNoneOption.any(options)- Returns the firstSome, orNoneif all areNone
License
MIT © @tr-yasuda
