Tiny library to improve error handling in TS using Rust-inspired types.
- Using npm -
npm install ts-res
- Using yarn -
yarn add ts-res
- Using pnpm -
pnpm add ts-res
Result type is convenient to use as a return type of a method that may fail.
In Result<T, E>
:
T
specifies success result type, which can be any typeE
specifies error type, which is constrained byError | string | void | undefined
.
Err()
and Ok()
are used as a return type enforcers.
function mayFail(isFailed: boolean): Result<number, Error> {
if (isFailed) {
return Err(new Error("Function failed"));
}
return Ok(123);
}
As a result we get an object which will either be {ok: true, data: T}
or {ok: false, error: E}
Note: Ok()
and Err()
can be used with no values if T
/E
were specified as void
or undefined
function getEmptyResult(isFailed: boolean): Result<void, void> {
if (isFailed) {
return Err();
}
return Ok();
}
Result<T, E>
can be handled manually:
const result = mayFail(true);
if (!result.ok) {
throw result.error;
}
console.log(result.data);
However it's much easier to use helper methods to quickly handle the result:
throw(message?: string)
- throws an error (with an optional custom error message) or returns an unwrapped resultor(value: T)
- returns an unwrapped result or a back-up valueelse(callback: (error: E) => T)
- returns an unwrapped result or executes callback that returns back-up value which can be based on provided errorand(callback: (result: T) => Result<T, E>)
- handles a result in a callback while ignoring an error, returns the result allowing for chaining
// -- throw()
mayFail(true).throw(); // returns 123
mayFail(false).throw(); // throws an Error with default message
mayFail(false).throw("My Message"); // throws an Error with "My Message"
// -- or()
mayFail(true).or(100); // returns 123
mayFail(false).or(100); // returns 100
// -- else()
mayFail(true).else((error) => 200); // returns 123
mayFail(false).else((error) => 200); // returns 200 (error can be used for some extra logic)
// -- and()
mayFail(true).and((result) => {
console.log(result);
}); // logs 123
mayFail(false).and((result) => {
console.log(result);
}); // doesn't do anything
More elaborate examples:
- Use
throw()
to unwrap the value after parsing a number
function toNumber(str: string): Result<number, string> {
const result = Number(str);
if (isNaN(result)) {
return Err("Couldn't parse a string");
}
return Ok(result);
}
const myNumber: number = toNumber("Hello").throw(); // Throws an Error
const myNumber: number = toNumber("123").throw(); // Returns 123
- Use
or()
to provide a back-up value while obtaining status code
function getStatusCode(statusCode: number): Result<number, string> {
if (statusCode > 200 && statusCode < 300) {
return Ok(statusCode);
}
return Err("Invalid status code");
}
function obtainStatus(): number {
return getStatusCode(response.statusCode).or(404); // Returns `statusCode` between 201 and 299 or 404
}
- Use
else()
andCustomError
to provide a back-up value based on error type
enum ErrorType {
A,
B,
C,
}
class CustomError extends Error {
private errorType: ErrorType;
constructor(type: ErrorType) {
super();
this.errorType = type;
}
get type(): ErrorType {
return this.errorType;
}
}
function myFunction(): Result<void, CustomError> {
//...
}
myFunction().else((error) => {
switch (error.type) {
case ErrorType.A:
return "a";
case ErrorType.B:
return "b";
case ErrorType.C:
return "c";
default:
break;
}
});
- Use
and()
to handle a result of writing data tolocalStorage
and ignore the error
function readLocalStorage(key: string): Result<string, void> {
const data = localStorage.getItem(key);
if (!data) {
return Err();
}
return Ok(data);
}
readLocalStorage.and((data) => {
const parsedData = JSON.parse(data);
console.log(parsedData);
})
- Use
and()
for handling http response data and chaining it with another method
type User = {
name: string;
age: number;
};
async function fetchUserData(): Promise<Result<User, Error>> {
const response = await fetch("https://api.example.com/user");
if (!response.ok) {
return Err(new Error("Failed to fetch user data"));
}
const data = await response.json();
return Ok(data);
}
const result = await fetchUserData();
result
.and((user) => {
console.log(user);
})
.else((error) => {
console.error(error);
});