Documentation Index
Fetch the complete documentation index at: https://mintlify.com/supermacro/neverthrow/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The fromPromise function transforms a Promise<T> that might reject into a ResultAsync<T, E>. It allows you to handle Promise rejections in a type-safe way by mapping the rejection to a known error type.
fromPromise is a top-level export that references ResultAsync.fromPromise.
Type Signature
function fromPromise<T, E>(
promise: PromiseLike<T>,
errorHandler: (unknownError: unknown) => E
): ResultAsync<T, E>
Parameters
promise: A PromiseLike<T> that might reject
errorHandler: Required function that maps the rejection value to a known error type E
- Receives
unknown as the error (the rejection value)
- Must return a value of type
E
Return Value
Returns a ResultAsync<T, E> that:
- Resolves to
Ok<T> if the promise resolves successfully
- Resolves to
Err<E> if the promise rejects
Basic Usage
Converting a Simple Promise
import { fromPromise } from 'neverthrow'
type FetchError = { type: 'NetworkError'; message: string }
const userPromise = fetch('https://api.example.com/user/1')
.then(res => res.json())
const userResult = fromPromise(
userPromise,
(error): FetchError => ({
type: 'NetworkError',
message: error instanceof Error ? error.message : 'Unknown error'
})
)
// userResult type: ResultAsync<User, FetchError>
const user = await userResult
if (user.isOk()) {
console.log('User:', user.value)
} else {
console.log('Error:', user.error.message)
}
Database Query
import { fromPromise } from 'neverthrow'
import { db } from './database'
type DbError = 'ConnectionFailed' | 'QueryFailed'
function getUser(id: string) {
return fromPromise(
db.query('SELECT * FROM users WHERE id = ?', [id]),
(): DbError => 'QueryFailed'
)
}
const result = await getUser('123')
Real-World Examples
HTTP Request with Detailed Error Handling
import { fromPromise, ResultAsync } from 'neverthrow'
type HttpError =
| { type: 'NetworkError'; message: string }
| { type: 'Timeout' }
| { type: 'InvalidResponse'; status: number }
async function fetchUser(id: string): ResultAsync<User, HttpError> {
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), 5000)
return fromPromise(
fetch(`https://api.example.com/users/${id}`, {
signal: controller.signal
}),
(error): HttpError => {
clearTimeout(timeout)
if (error instanceof Error && error.name === 'AbortError') {
return { type: 'Timeout' }
}
return {
type: 'NetworkError',
message: error instanceof Error ? error.message : 'Unknown error'
}
}
).andThen(response => {
clearTimeout(timeout)
if (!response.ok) {
return err({ type: 'InvalidResponse', status: response.status })
}
return fromPromise(
response.json(),
(): HttpError => ({ type: 'NetworkError', message: 'Invalid JSON' })
)
})
}
File System Operations
import { fromPromise } from 'neverthrow'
import { promises as fs } from 'fs'
type FileError =
| { type: 'NotFound'; path: string }
| { type: 'PermissionDenied'; path: string }
| { type: 'Unknown'; message: string }
function readFile(path: string) {
return fromPromise(
fs.readFile(path, 'utf-8'),
(error): FileError => {
if (error instanceof Error) {
if (error.message.includes('ENOENT')) {
return { type: 'NotFound', path }
}
if (error.message.includes('EACCES')) {
return { type: 'PermissionDenied', path }
}
return { type: 'Unknown', message: error.message }
}
return { type: 'Unknown', message: 'Unknown error' }
}
)
}
const result = await readFile('config.json')
result.match(
(content) => console.log('File content:', content),
(error) => {
switch (error.type) {
case 'NotFound':
console.log('File not found:', error.path)
break
case 'PermissionDenied':
console.log('Permission denied:', error.path)
break
case 'Unknown':
console.log('Error:', error.message)
break
}
}
)
Multiple Async Operations
import { fromPromise, safeTry } from 'neverthrow'
type AppError = 'DbError' | 'ApiError' | 'ValidationError'
function processOrder(orderId: string) {
return safeTry(async function*() {
// Fetch order from database
const order = yield* fromPromise(
db.getOrder(orderId),
(): AppError => 'DbError'
)
// Validate order
const validated = yield* validateOrder(order)
.mapErr((): AppError => 'ValidationError')
// Call payment API
const payment = yield* fromPromise(
paymentApi.charge(validated),
(): AppError => 'ApiError'
)
// Update order in database
yield* fromPromise(
db.updateOrder(orderId, { status: 'paid', paymentId: payment.id }),
(): AppError => 'DbError'
)
return ok(payment)
})
}
Promise.all with Results
import { fromPromise, ResultAsync } from 'neverthrow'
type FetchError = { message: string }
function fetchMultipleUsers(ids: string[]) {
const promises = ids.map(id =>
fromPromise(
fetch(`https://api.example.com/users/${id}`).then(r => r.json()),
(error): FetchError => ({
message: error instanceof Error ? error.message : 'Fetch failed'
})
)
)
return ResultAsync.combine(promises)
}
const result = await fetchMultipleUsers(['1', '2', '3'])
if (result.isOk()) {
console.log('All users:', result.value) // User[]
} else {
console.log('At least one fetch failed:', result.error)
}
Streaming Response
import { fromPromise } from 'neverthrow'
type StreamError = 'StreamFailed' | 'ReadFailed'
function downloadFile(url: string) {
return fromPromise(
fetch(url),
(): StreamError => 'StreamFailed'
).andThen(response => {
if (!response.ok) {
return err('StreamFailed' as const)
}
return fromPromise(
response.arrayBuffer(),
(): StreamError => 'ReadFailed'
)
})
}
Chaining with Other Methods
import { fromPromise } from 'neverthrow'
const result = fromPromise(
fetch('https://api.example.com/data'),
() => 'FetchError' as const
)
.andThen(response => {
if (!response.ok) {
return err('InvalidResponse' as const)
}
return fromPromise(
response.json(),
() => 'ParseError' as const
)
})
.map(data => data.items)
.map(items => items.filter(item => item.active))
// result type: ResultAsync<Item[], 'FetchError' | 'InvalidResponse' | 'ParseError'>
Important Considerations
fromPromise vs fromAsyncThrowableUse fromPromise when you already have a Promise instance:const promise = someAsyncFunction()
const result = fromPromise(promise, handleError)
Use fromAsyncThrowable when wrapping a function that might throw synchronously:const safeFunction = fromAsyncThrowable(someAsyncFunction, handleError)
const result = safeFunction() // Safe from sync throws
Error Handler is Required
Unlike fromThrowable, the error handler parameter is not optional for fromPromise. This is because:
- Promise rejections can be of any type
- TypeScript cannot infer a safe default error type
- You must explicitly handle the unknown rejection value
// This will cause a TypeScript error
const result = fromPromise(somePromise) // Error: Expected 2 arguments
// You must provide an error handler
const result = fromPromise(somePromise, () => 'Error')
Key Points
- Converts
Promise<T> to ResultAsync<T, E>
- Error handler parameter is required
- Maps promise rejections to a known error type
- Use for existing Promise instances
- For function wrapping, prefer
fromAsyncThrowable
- Works seamlessly with
safeTry and other Result methods