Try
Type-safe error handling primitives for modern JavaScript & TypeScript projects.
Installation
Using npm:
npm install @asleepace/try
Using Yarn:
yarn add @asleepace/try
Using Bun:
bun add @asleepace/try
Quick Start
The goal of this package is to provide concise, type-safe and easy to use tools for handling exceptions.
import { Try } from '@asleepace/try'
const [url, error] = Try.catch(() => new URL(userInput))
if (error) return console.warn(error.message)
const [response, networkError] = await Try.catch(() => fetch(url))
const [jsonData, parsingError] = await Try.catch(() => response!.json())
if (parsingError) return console.warn(parsingError.message)
return jsonData.userName
Result Type
The Try.catch(fn)
utility returns a special result type Res
which is a class that extends a result tuple. The Res
class provides
some helpful utilities for handling results:
const result = await Try.catch(() => response.json())
// includes powerful type-guards
if (result.isErr()) return console.warn(result.error.message)
// and convenience methods
const json = result.unwrap()
// supports array destructuring
const [user, error] = await Try.catch(async () => {
const profileUrl = new URL(json.user!.profile)
return fetch(profileUrl).then((res) => res.json())
})
if (!error) {
console.log(`Hello, ${user.name}!`)
} else {
console.warn(error.message)
}
The can be used in conjunction with the array destructuring syntax, which can help if you are only interested in the value or error.
const result = await Try.catch(() =>
fetch('https://api.users.com/me')
.then((res) => res.json())
.then((usr) => usr as User)
)
console.log(result.value) // User | undefined
console.log(result.error) // Error | undefined
// automatically narrows the type for you!
if (result.isOk()) {
const user = result.value // User!
} else {
const fail = result.error // Error!
}
// in some cases you may just want the value or to throw again:
const user = result.unwrap() // User | never
// or you may want the value or a fallback:
const user = result.unwrapOr(cachedUser) // User
API
Try.catch<T>(fn: () => Promise<T>): Promise<[T, undefined] | [undefined, Error]>
Executes a function and returns a tuple containing either:
[value, undefined]
if the function executes successfully[undefined, error]
if the function throws an error
Works with both synchronous and asynchronous functions, automatically returning a Promise for async operations.
Shorthand
This package also exports a shorthand utility called vet which stands for value / error tuple and provides a more concise way to interact with this api.
// Add more examples of the VET shorthand
import { vet } from '@asleepace/try'
// Simple usage
const [value] = vet(() => JSON.parse(data))
// Only get the error
const [, error] = vet(() => JSON.parse(data))
// With TypeScript generics for better type inference
const [user] = vet<User>(() => getUserFromAPI())
Benefits
- No Try/Catch Blocks: Clean, readable code without nested try/catch structures
- Type Safety: Full TypeScript support with proper type inference
- Consistent Pattern: Uniform error handling for both sync and async code
- Zero Dependencies: Lightweight and dependency-free
- Isomorphic: Works in both browser and Node.js environments
- Powerful Results: Type safe, tested and versatile result class
Testing
This package includes a comprehensive test suite. To run the tests:
# Clone the repository
git clone https://github.com/asleepace/try.git
cd try
# Install dependencies
bun install
# Build project & generate types
bun run build
# Run tests
bun test
Examples
Making Network Requests
// handle synchronous operations which can throw with ease...
const encoded = Try.catch(() => JSON.stringify(userInput))
if (!encoded.ok) return encoded.error
const [response, networkError] = await Try.catch(() =>
fetch('https://api.com/create', {
method: 'POST',
body: encoded.value,
})
)
if (networkError) return networkError
const [user, jsonError] = await Try.catch(response.json)
if (jsonError) return jsonError
return user
Error Handling in a React Component
import React, { useEffect, useState } from 'react'
import { Try } from '@asleepace/try'
function UserProfile({ userId }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
async function loadUser() {
setLoading(true)
const [userData, fetchError] = await Try.catch(async () => {
const response = await fetch(`/api/users/${userId}`)
if (!response.ok) throw new Error(`HTTP error ${response.status}`)
return response.json()
})
setUser(userData)
setError(fetchError)
setLoading(false)
}
loadUser()
}, [userId])
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
)
}
Chaining Operations
import { Try } from '@asleepace/try'
async function processData(rawData): object | undefined {
// Step 1: Parse the data
const [parsed, parseError] = Try.catch(() => JSON.parse(rawData))
if (parseError) return console.warn(parseError.message)
// Step 2: Transform the data
const [transformed, transformError] = Try.catch(() => {
return parsed.items.map((item) => ({
id: item.id,
name: item.name.toUpperCase(),
value: item.value * 2,
}))
})
if (transformError) return console.warn(transformError.message)
// Step 3: Save the data
const saveResult = await Try.catch(async () => {
const response = await fetch('/api/save', {
body: JSON.stringify(transformed),
method: 'POST',
})
return response.json()
})
if (saveResult.isErr()) return console.warn(saveResult.error)
return saveResult.unwrap()
}
Specification
Overview
The Try.catch
utility provides a type-safe way to handle errors in both synchronous and asynchronous functions, returning a result tuple that contains either a value or an error, but never both.
Return Values
Try.catch
always returns either aResultOk<T>
orResultError
type, which extends the tuple types[T, undefined]
or[undefined, Error]
respectively.- The returned object is guaranteed to have exactly two elements, with either index 0 or index 1 being undefined.
- If the function completes successfully, index 0 will contain the return value and index 1 will be undefined.
- If the function throws an error, index 0 will be undefined and index 1 will contain the error.
Error Handling
- If a non-Error value is thrown (such as a string, number, or object), it will be automatically converted to an instance of the built-in
Error
class. - Thrown values that aren't already errors will first be coerced to strings and then used to construct a new
Error
instance. - If the function returns an
Error
object as its value (not thrown), it will be treated as a successful result with theError
as the value at index 0, not as an error case.
Properties and Methods
The returned result object includes several convenience properties and methods:
.value
: Returns the value at index 0 (the success value) or undefined if an error occurred..error
: Returns the error at index 1 or undefined if the operation was successful..ok
: A boolean property that istrue
when.error === undefined
andfalse
otherwise..unwrap()
: Returns the value if present, or throws the captured error if no value is present..unwrapOr(fallback)
: Returns the value if present, or the provided fallback value if no value is present..toString()
: Returns a string representation of the result, displaying either the successful value or the error message.
Type Safety
- The returned result type correctly narrows in type-guard contexts:
- When checking
if (result.ok)
, TypeScript will narrow the type toResultOk<T>
. - When checking
if (!result.ok)
, TypeScript will narrow the type toResultError
.
- When checking
- In
ResultOk<T>
contexts,result.value
is typed asT
andresult.error
is typed asundefined
. - In
ResultError
contexts,result.value
is typed asundefined
andresult.error
is typed asError
.
Async Support
Try.catch
supports both synchronous and asynchronous functions.- When passed a function that returns a Promise,
Try.catch
returns a Promise that resolves to a result tuple. - Rejected promises are captured and converted to error results, following the same rules as thrown errors.
Instanceof Support
- The returned result object is an instance of
TryResultClass
, enablinginstanceof
checks. - This allows for easier type checking and integration with existing code patterns.
Changelog
0.2.1
- add
Res.ok(value)
andRes.err(error)
initializers - add
isOk()
andisErr()
type guards to result - move all code into
src/index.ts
- remove junks files and reduce package size
- remove internal
Try.#handler
method - simplify
Try.catch
logic - simplify types
0.2.0
- add
vet(fn)
shorthand - add convenience methods
- Update
Result
class - Update test suite
0.14.0
- Update
README.md
- Update
Try.catch
functionality
0.13.0
- Initial release
License
MIT