Overview
Converts an async function that might throw into a function that returns a ResultAsync. This is the safest way to handle third-party async functions.
Signature
static fromThrowable<A extends readonly any[], R, E>(
fn: (...args: A) => Promise<R>,
errorFn?: (err: unknown) => E
): (...args: A) => ResultAsync<R, E>
fn
(...args: A) => Promise<R>
required
An async function that might throw or return a rejecting Promise
Optional function to map thrown errors to a known type. Defaults to returning the error as-is.
Returns: A new function with the same parameters that returns ResultAsync<R, E>
Usage
Basic usage
import { ResultAsync } from 'neverthrow'
type DbError = { message: string }
const safeQuery = ResultAsync.fromThrowable(
async (sql: string) => await db.query(sql),
(e): DbError => ({ message: String(e) })
)
const result = await safeQuery('SELECT * FROM users')
// result is Result<QueryResult, DbError>
File operations
import fs from 'fs/promises'
const safeReadFile = ResultAsync.fromThrowable(
fs.readFile,
(error) => `Failed to read file: ${error}`
)
const content = await safeReadFile('config.json', 'utf-8')
if (content.isOk()) {
console.log(content.value)
}
API calls
type ApiError = { status: number, message: string }
const safeFetch = ResultAsync.fromThrowable(
async (url: string) => {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
return response.json()
},
(e): ApiError => ({
status: 500,
message: e instanceof Error ? e.message : String(e)
})
)
const data = await safeFetch('/api/users')
Database operations
interface User {
id: number
email: string
}
const findUser = ResultAsync.fromThrowable(
async (email: string): Promise<User> => {
const user = await db.users.findUnique({ where: { email } })
if (!user) throw new Error('User not found')
return user
},
(e) => e instanceof Error ? e.message : 'Unknown error'
)
const user = await findUser('[email protected]')
Why use fromThrowable over fromPromise?
fromThrowable is safer because it catches both synchronous throws and Promise rejections.
// This function can throw synchronously OR return a rejected Promise
const dangerousFunction = async (id: number) => {
if (id < 0) {
throw new Error('Invalid ID') // Synchronous throw!
}
return fetch(`/api/${id}`) // May reject
}
// ❌ UNSAFE - fromPromise doesn't catch synchronous throws
const unsafe = ResultAsync.fromPromise(
dangerousFunction(-1), // Throws immediately!
(e) => String(e)
)
// ✅ SAFE - fromThrowable catches both
const safe = ResultAsync.fromThrowable(
dangerousFunction,
(e) => String(e)
)
const result = await safe(-1) // Returns Err, doesn't throw
Key characteristics
- Catches all errors: Both sync throws and Promise rejections
- Preserves parameters: Wrapped function has same signature
- Type safety: Error type is controlled by errorFn
- Reusable: Create the wrapper once, use many times
Comparison table
| Method | Catches sync throws | Catches async rejects | Use case |
|---|
fromThrowable | ✅ Yes | ✅ Yes | Wrapping functions |
fromPromise | ❌ No | ✅ Yes | Wrapping Promises |
fromSafePromise | ❌ No | ❌ No | Promises that never reject |
Top-level export
Also available as a standalone function:
import { fromAsyncThrowable } from 'neverthrow'
const safeFn = fromAsyncThrowable(
myAsyncFunction,
(e) => `Error: ${e}`
)
Result.fromThrowable
Synchronous version
from-promise
Alternative for Promise objects