A minimal, functional, and tree-shakable Result library for TypeScript that prioritizes simplicity and serialization
npm install tuple-resulttuple-result is a minimal, functional, and tree-shakable Result library for TypeScript that prioritizes simplicity and serialization.
- ๐ฎ Simple, declarative API: Intuitive array destructuring with full type safety
- ๐ Lightweight & Tree Shakable: Function-based design with ~150B core
- โก High Performance: Minimal overhead with just a 3-element array
- ๐ Easy Serialization: Simple array format perfect for wire transmission
- ๐ฆ Zero Dependencies: Standalone library ensuring ease of use in various environments
- ๐ง Functional Helpers: Powerful map, unwrap, and utility functions
- ๐งต Type Safe: Full TypeScript support with literal types and type guards
- React Router v7 (CodeSandbox)
Build a minimal, functional Result library that prioritizes simplicity and serialization. While libraries like ts-results and neverthrow offer robust features, their class-based implementations can create challenges with serialization and bundle size. tuple-result provides a functional alternative using simple arrays - combining minimal overhead (~150B core), easy serialization for APIs and frameworks like React Router, and helper functions while adhering to the KISS principle.
tuple-result provides a simple approach to error handling. Here's how to use it:
``ts
import { Err, Ok } from 'tuple-result';
const success = Ok(42);
const failure = Err('Something went wrong');
`
`ts
// Method-based approach
if (success.isOk()) {
console.log(success.value); // 42
}
// Array destructuring approach
const [ok, error, value] = success;
if (ok) {
console.log(value); // 42
}
// Direct unwrapping (throws on error)
const value = success.unwrap(); // 42
`
`ts
import { t, tAsync } from 'tuple-result';
// Wrap synchronous functions
const result = t(() => JSON.parse('invalid')); // Err(SyntaxError)
// Wrap promises
const asyncResult = await tAsync(fetch('/api/data')); // Ok(Response) or Err(Error)
`
`ts
import { unwrapErr, unwrapOr } from 'tuple-result';
// Provide defaults
const value = unwrapOr(failure, 0); // 0
`
`ts
import { mapErr, mapOk } from 'tuple-result';
// Transform success values
const doubled = mapOk(success, (x) => x * 2); // Ok(84)
// Transform errors
const wrapped = mapErr(failure, (e) => Error: ${e}); // Err('Error: Something went wrong')`
`ts
import { match } from 'tuple-result';
// Clean conditional logic
const message = match(result, {
ok: (value) => Success: ${value},Error: ${error}
err: (error) =>
});
// Complex transformations
const processed = match(result, {
ok: (user) => ({ ...user, displayName: user.name.toUpperCase() }),
err: (error) => ({ id: 0, name: 'Unknown', error: error.message })
});
`
`ts
// Convert to serializable format
const serialized = success.toArray(); // [true, undefined, 42]
// Reconstruct from serialized format
const reconstructed = fromArray(serialized); // Back to TResult with methods
`
#### Ok
Creates a successful result containing the given value.
`ts`
const result = Ok(42);
console.log(result.unwrap()); // 42
console.log(result.isOk()); // true
#### Err
Creates an error result containing the given error.
`ts`
const result = Err('Something went wrong');
console.log(result.isErr()); // true
console.log(result.error); // 'Something went wrong'
#### isOk
Type guard to check if a result is successful.
`ts`
if (isOk(result)) {
// TypeScript knows result is OkResult here
console.log(result.unwrap());
}
#### isErr
Type guard to check if a result is an error.
`ts`
if (isErr(result)) {
// TypeScript knows result is ErrResult here
console.log(result.error);
}
#### unwrap
Extracts the value from a result, throwing if it's an error.
`ts`
try {
const value = unwrap(success); // 42
} catch (error) {
// Handle error
}
#### unwrapOk
Extracts the value from an Ok result, throwing if it's an error.
`ts`
const value = unwrapOk(success); // 42
#### unwrapErr
Extracts the error from an Err result, throwing if it's successful.
`ts`
const error = unwrapErr(failure); // 'Something went wrong'
#### unwrapOr
Extracts the value from a result, returning a default if it's an error.
`ts`
const value = unwrapOr(failure, 0); // 0
#### unwrapOrNull
Extracts the value from a result, returning null if it's an error.
`ts`
const value = unwrapOrNull(failure); // null
#### mapOk
Maps the value inside an Ok result using the provided function.
`ts`
const doubled = mapOk(Ok(21), (x) => x * 2); // Ok(42)
#### mapErr
Maps the error inside an Err result using the provided function.
`tsHTTP ${code}
const wrapped = mapErr(Err(404), (code) => ); // Err('HTTP 404')`
#### match
Pattern matches on a result, calling the appropriate handler. Similar to Rust's match! macro.
`tsSuccess: ${value}
const message = match(result, {
ok: (value) => ,Error: ${error}
err: (error) => `
});
#### t
Wraps a synchronous function call in a Result.
`ts`
const result = t(() => JSON.parse('invalid')); // Err(SyntaxError)
const safeDivide = (a: number, b: number) => t(() => a / b, a, b);
#### tAsync
Wraps a Promise in a Result.
`ts`
const result = await tAsync(fetch('/api/data')); // Ok(Response) or Err(Error)
#### toArray() (Instance Method)
Converts a result to a plain array for serialization.
`ts`
const result = Ok(42);
const serialized = result.toArray(); // [true, undefined, 42]
#### fromArray
Creates a result instance from a plain array.
`ts`
const result = fromArray([true, undefined, 42]); // Ok(42) with methods
TResultArray is a subset of TResult - same array structure, but TResult adds convenience methods.
- TResult: Full-featured classes with .isOk(), .unwrap(), .value methodsTResultArray
- : Plain arrays perfect for serialization (React Router, APIs, JSON)
Key benefit: All helper functions work with both types seamlessly.
`typescript
const classResult = Ok('hello');
const arrayResult = [true, undefined, 'hello'] as const;
isOk(classResult); // โ
works
isOk(arrayResult); // โ
also works
`
Use TResult by default. You get TResultArray from:
- React Router loaders: useLoaderData()JSON.parse(response)
- JSON parsing:
- API responses
For serialization: result.toArray() โ send over network โ use helpers directly on received arrays or deserialize using fromArray(result).
No conversion needed - helpers work with both!
TypeScript compatibility. Since TResult (classes) and TResultArray (plain arrays) have the same structure but different types, overloads ensure all helper functions work seamlessly with both:
`typescript`
// These all work the same way
unwrapOr(Ok(42), 0); // โ
TResult
unwrapOr([true, undefined, 42], 0); // โ
TResultArray
unwrapOr(someResult, 0); // โ
Either type
Without overloads, complex types can cause TypeScript errors:
`typescript``
// โ Sometimes fails with complex types
const result: TResultArray
unwrapOr(result, defaultUser); // Type 'TResultArray
Overloads ensure compatibility in all scenarios.
- try operator proposal - ECMAScript proposal that inspired our array destructuring
- ts-results
- neverthrow