
Security News
pnpm 11.5 Adds Support for Recognizing npm Staged Publishes
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.
@crvouga/result
Advanced tools
A comprehensive Result type library for TypeScript with Ok, Err, Loading, and NotAsked states
A comprehensive JavaScript library for handling success and error states with support for remote data patterns. Provides type-safe error handling, pattern matching, and functional programming utilities.
Ok and Err typesLoading and NotAsked statesmap, flatMap, bimap, swaptoPromisefromNullable and fromUndefinednpm install @crvouga/result
import { Ok, Err, Loading, NotAsked, match, mapOk, flatMapOk } from '@crvouga/result';
// Basic usage
const result = Ok(42);
const doubled = mapOk(result, x => x * 2); // Ok(84)
// Error handling
const errorResult = Err('Something went wrong');
const message = match(errorResult, {
ok: value => `Success: ${value}`,
err: error => `Error: ${error}`,
loading: () => 'Loading...',
notAsked: () => 'Not started'
});
// Remote data patterns
const userData = Loading();
if (isLoading(userData)) {
console.log('Loading user data...');
}
Ok<T> - Represents a successful result with a value of type TErr<E> - Represents an error result with an error of type ELoading - Represents a loading state (for remote data)NotAsked - Represents an initial state (for remote data)type Result<T, E> = Ok<T> | Err<E>;
type RemoteResult<T, E> = Ok<T> | Err<E> | Loading | NotAsked;
Ok(value)Creates a successful result.
const result = Ok(42);
console.log(result); // { type: "ok", value: 42 }
Err(error)Creates an error result.
const result = Err('Something went wrong');
console.log(result); // { type: "err", error: "Something went wrong" }
Loading()Creates a loading state.
const result = Loading();
console.log(result); // { type: "loading" }
NotAsked()Creates a not-asked state.
const result = NotAsked();
console.log(result); // { type: "not-asked" }
isOk(result, validator?)Checks if a result is successful, with optional value validation.
const result = Ok(42);
console.log(isOk(result)); // true
// With validation
const isNumber = (value) => typeof value === 'number';
console.log(isOk(result, isNumber)); // true
console.log(isOk(Ok('hello'), isNumber)); // false
isErr(result, validator?)Checks if a result is an error, with optional error validation.
const result = Err('Network error');
console.log(isErr(result)); // true
// With validation
const isString = (error) => typeof error === 'string';
console.log(isErr(result, isString)); // true
console.log(isErr(Err(404), isString)); // false
isLoading(result)Checks if a result is in loading state.
const result = Loading();
console.log(isLoading(result)); // true
isNotAsked(result)Checks if a result is in not-asked state.
const result = NotAsked();
console.log(isNotAsked(result)); // true
isResult(value, valueValidator?, errorValidator?)Comprehensive type guard for Result types with optional validation.
const result = Ok(42);
console.log(isResult(result)); // true
// With validation
const isNumber = (value) => typeof value === 'number';
const isString = (error) => typeof error === 'string';
console.log(isResult(result, isNumber, isString)); // true
isRemoteResult(value, valueValidator?, errorValidator?)Type guard for RemoteResult types with optional validation.
const result = Loading();
console.log(isRemoteResult(result)); // true
match(result, matchers)Pattern matching for all RemoteResult states.
const result = Ok(42);
const message = match(result, {
ok: value => `Success: ${value}`,
err: error => `Error: ${error}`,
loading: () => 'Loading...',
notAsked: () => 'Not started'
});
console.log(message); // "Success: 42"
fold(result, onOk, onErr)Pattern matching for basic Result types (Ok/Err only).
const result = Ok(42);
const message = fold(result,
value => `Success: ${value}`,
error => `Error: ${error}`
);
console.log(message); // "Success: 42"
foldRemote(result, matchers)Pattern matching for RemoteResult types with semantic naming.
const result = Ok(42);
const message = foldRemote(result, {
success: value => `Success: ${value}`,
failure: error => `Error: ${error}`,
loading: () => 'Loading...',
notAsked: () => 'Not started'
});
console.log(message); // "Success: 42"
mapOk(result, mapper)Maps a function over the value of a successful result.
const result = Ok(42);
const doubled = mapOk(result, x => x * 2);
console.log(doubled); // { type: "ok", value: 84 }
mapErr(result, mapper)Maps a function over the error of an error result.
const result = Err('network error');
const formatted = mapErr(result, error => `Error: ${error.toUpperCase()}`);
console.log(formatted); // { type: "err", error: "Error: NETWORK ERROR" }
flatMapOk(result, mapper)Maps a function that returns a result over a successful result.
const result = Ok(5);
const processed = flatMapOk(result, x => x > 10 ? Ok(x) : Err('Too small'));
console.log(processed); // { type: "err", error: "Too small" }
flatMapErr(result, mapper)Maps a function that returns a result over an error result.
const result = Err('network error');
const recovered = flatMapErr(result, error =>
error.includes('network') ? Ok('Using cached data') : Err(error)
);
console.log(recovered); // { type: "ok", value: "Using cached data" }
bimap(result, okMapper, errMapper)Maps both success and error cases in one operation.
const result = Ok(42);
const transformed = bimap(result,
value => value * 2,
error => `Error: ${error}`
);
console.log(transformed); // { type: "ok", value: 84 }
mapRemote(result, mapper)Maps over successful RemoteResults, passing through other states unchanged.
const result = Ok({ id: 1, name: 'John' });
const mapped = mapRemote(result, user => user.name);
console.log(mapped); // { type: "ok", value: "John" }
unwrap(result)Extracts the value from a successful result, throws on error.
const result = Ok(42);
const value = unwrap(result); // 42
const errorResult = Err('error');
unwrap(errorResult); // throws "error"
unwrapOr(result, defaultValue)Extracts the value from a successful result, or returns a default.
const result = Ok(42);
const value = unwrapOr(result, 0); // 42
const errorResult = Err('error');
const value = unwrapOr(errorResult, 0); // 0
getOrElse(result, defaultValue)Alias for unwrapOr for consistency with functional libraries.
const result = Ok(42);
const value = getOrElse(result, 0); // 42
fromNullable(value, error)Creates a Result from a nullable value.
const user = getUserFromCache();
const result = fromNullable(user, 'User not found in cache');
// Returns Ok(user) if user is not null/undefined, otherwise Err('User not found in cache')
fromUndefined(value, error)Creates a Result from a value that might be undefined.
const config = process.env.API_KEY;
const result = fromUndefined(config, 'API key not configured');
// Returns Ok(config) if config is not undefined, otherwise Err('API key not configured')
toPromise(result)Converts a Result to a Promise.
const result = Ok(42);
const promise = toPromise(result);
const value = await promise; // 42
const errorResult = Err('Something went wrong');
const promise = toPromise(errorResult);
try {
await promise; // throws 'Something went wrong'
} catch (error) {
console.log(error); // 'Something went wrong'
}
swap(result)Swaps Ok and Err values of a Result.
const result = Ok(42);
const swapped = swap(result);
console.log(swapped); // { type: "err", error: 42 }
const errorResult = Err('Something went wrong');
const swapped = swap(errorResult);
console.log(swapped); // { type: "ok", value: "Something went wrong" }
equals(a, b, valueEquals?)Checks if two Results are equal. Safely handles unknown parameters and supports custom equality functions.
const a = Ok(42);
const b = Ok(42);
console.log(equals(a, b)); // true
const c = Err('error');
const d = Err('error');
console.log(equals(c, d)); // true
// Safe with unknown values
console.log(equals(null, Ok(42))); // false
console.log(equals({}, Ok(42))); // false
// With custom equality function for objects
const objA = Ok({ id: 1, name: 'John' });
const objB = Ok({ id: 1, name: 'John' });
console.log(equals(objA, objB)); // false (reference equality)
const deepEquals = (a, b) => JSON.stringify(a) === JSON.stringify(b);
console.log(equals(objA, objB, deepEquals)); // true
// With custom equality for arrays
const arrA = Ok([1, 2, 3]);
const arrB = Ok([1, 2, 3]);
const arrayEquals = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
console.log(equals(arrA, arrB, arrayEquals)); // true
fromNullish(value, error)Creates a Result from a value that might be null or undefined. Returns Ok(value) if the value is not null/undefined, otherwise Err(error).
const result = fromNullish('hello', 'Value is nullish');
// { type: 'ok', value: 'hello' }
const result2 = fromNullish(null, 'Value is nullish');
// { type: 'err', error: 'Value is nullish' }
const result3 = fromNullish(undefined, 'Value is nullish');
// { type: 'err', error: 'Value is nullish' }
fromFalsy(value, error)Creates a Result from a value that might be falsy. Returns Ok(value) if the value is truthy, otherwise Err(error).
const result = fromFalsy('hello', 'Value is falsy');
// { type: 'ok', value: 'hello' }
const result2 = fromFalsy('', 'Value is falsy');
// { type: 'err', error: 'Value is falsy' }
const result3 = fromFalsy(0, 'Value is falsy');
// { type: 'err', error: 'Value is falsy' }
const result4 = fromFalsy(false, 'Value is falsy');
// { type: 'err', error: 'Value is falsy' }
const result5 = fromFalsy(null, 'Value is falsy');
// { type: 'err', error: 'Value is falsy' }
const result6 = fromFalsy(undefined, 'Value is falsy');
// { type: 'err', error: 'Value is falsy' }
tryCatchSync(fn)Wraps a synchronous function that might throw.
const result = tryCatchSync(() => {
const value = JSON.parse('invalid json');
return value;
});
console.log(result); // { type: "err", error: SyntaxError }
tryCatch(fn)Wraps an asynchronous function that might throw.
const result = await tryCatch(async () => {
const response = await fetch('/api/data');
return response.json();
});
isRemoteSuccess(result, validator?)Checks if a value is a successful RemoteResult (Ok only).
const result = Ok(42);
console.log(isRemoteSuccess(result)); // true
const loadingResult = Loading();
console.log(isRemoteSuccess(loadingResult)); // false
isRemoteFailure(result, validator?)Checks if a value is a failed RemoteResult (Err only).
const result = Err('error');
console.log(isRemoteFailure(result)); // true
const successResult = Ok(42);
console.log(isRemoteFailure(successResult)); // false
import { Ok, Err, isOk, isErr, unwrapOr } from '@crvouga/result';
function divide(a, b) {
if (b === 0) {
return Err('Division by zero');
}
return Ok(a / b);
}
const result = divide(10, 2);
if (isOk(result)) {
console.log(`Result: ${result.value}`); // Result: 5
} else {
console.log(`Error: ${result.error}`);
}
// Or use unwrapOr for default values
const value = unwrapOr(result, 0);
import { Ok, Err, Loading, match, fromNullable } from '@crvouga/result';
async function fetchUser(id) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
return Err(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return fromNullable(data.user, 'User not found');
} catch (error) {
return Err(`Network error: ${error.message}`);
}
}
// In your component
const userData = Loading(); // Initial state
const renderUser = (data) => {
return match(data, {
ok: user => <UserCard user={user} />,
err: error => <ErrorMessage error={error} />,
loading: () => <Spinner />,
notAsked: () => <button onClick={fetchUser}>Load User</button>
});
};
import { Ok, Err, fromNullable, flatMapOk } from '@crvouga/result';
function validateEmail(email) {
return fromNullable(email, 'Email is required')
|> (r) => flatMapOk(r, email =>
email.includes('@') ? Ok(email) : Err('Invalid email format')
);
}
function validateAge(age) {
return fromNullable(age, 'Age is required')
|> (r) => flatMapOk(r, age =>
age >= 18 ? Ok(age) : Err('Must be 18 or older')
);
}
const emailResult = validateEmail('john@example.com');
const ageResult = validateAge(25);
if (isOk(emailResult) && isOk(ageResult)) {
console.log('Form is valid');
} else {
console.log('Validation errors:', {
email: emailResult.type === 'err' ? emailResult.error : null,
age: ageResult.type === 'err' ? ageResult.error : null
});
}
import { Ok, Err, toPromise, tryCatch } from '@crvouga/result';
// Convert Results to Promises
const result = Ok(42);
const promise = toPromise(result);
const value = await promise; // 42
// Wrap async operations
const fetchData = async () => {
const result = await tryCatch(async () => {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
});
return result;
};
const data = await fetchData();
if (isOk(data)) {
console.log('Data:', data.value);
} else {
console.error('Error:', data.error);
}
import { Ok, Err, match, bimap, swap } from '@crvouga/result';
function processOrder(order) {
// Validate order
if (!order.items || order.items.length === 0) {
return Err('Order must contain at least one item');
}
if (order.total <= 0) {
return Err('Order total must be positive');
}
// Apply business rules
const processedOrder = {
...order,
status: order.total > 100 ? 'premium' : 'standard',
discount: order.total > 200 ? 0.1 : 0
};
return Ok(processedOrder);
}
const order = { items: ['laptop'], total: 150 };
const result = processOrder(order);
// Transform the result for display
const displayResult = bimap(result,
order => `Order processed: ${order.status} (${order.discount * 100}% discount)`,
error => `Order failed: ${error}`
);
// Or use pattern matching for complex logic
const message = match(result, {
ok: order => {
if (order.status === 'premium') {
return `🎉 Premium order confirmed! Total: $${order.total}`;
}
return `✅ Order confirmed! Total: $${order.total}`;
},
err: error => {
if (error.includes('items')) {
return `🛒 ${error}. Please add items to your cart.`;
}
return `❌ ${error}`;
},
loading: () => '⏳ Processing your order...',
notAsked: () => '🛒 Add items to cart to continue'
});
See CONTRIBUTING.md for details on how to contribute to this project.
MIT License - see LICENSE for details.
See CHANGELOG.md for a list of changes and version history.
FAQs
A comprehensive Result type library for TypeScript with Ok, Err, Loading, and NotAsked states
We found that @crvouga/result demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

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.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.

Security News
Federal audit finds NIST lacked a plan to clear the NVD backlog, wasted funds on duplicate work, and delayed use of CISA data.

Research
/Security News
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.