Skip to content

Instantly share code, notes, and snippets.

@seanaguinaga
Forked from mohilcode/result.ts
Created April 22, 2025 17:32
Show Gist options
  • Save seanaguinaga/b0c006aef408b42f96d559aa3a6e3b83 to your computer and use it in GitHub Desktop.
Save seanaguinaga/b0c006aef408b42f96d559aa3a6e3b83 to your computer and use it in GitHub Desktop.
typescript implementation of the result pattern for functional error handling. provides type-safe alternatives to try/catch with monadic operations for composition. use this to make error handling explicit in your function signatures and avoid throwing exceptions
export type Success<T> = {
readonly type: 'success'
readonly data: T
readonly error?: never
}
export type Failure<E> = {
readonly type: 'failure'
readonly data?: never
readonly error: E
}
export type Result<T, E = ErrorInfo> = Success<T> | Failure<E>
export type ErrorInfo = {
readonly message: string
readonly code?: string
readonly stack?: string
readonly cause?: unknown
}
export const isSuccess = <T, E>(result: Result<T, E>): result is Success<T> => {
return result.type === 'success'
}
export const isFailure = <T, E>(result: Result<T, E>): result is Failure<E> => {
return result.type === 'failure'
}
const createErrorInfo = (error: unknown): ErrorInfo => {
if (error instanceof Error) {
return {
message: error.message,
stack: error.stack,
cause: error.cause,
}
}
return {
message: String(error),
cause: error,
}
}
export const success = <T>(data: T): Success<T> => {
return { type: 'success', data }
}
export const failure = <E>(error: E): Failure<E> => {
return { type: 'failure', error }
}
export const tryCatch = async <T>(
promiseFn: () => Promise<T> | Promise<T>
): Promise<Result<T, ErrorInfo>> => {
try {
const data = await (typeof promiseFn === 'function' ? promiseFn() : promiseFn)
return success(data)
} catch (error) {
return failure(createErrorInfo(error))
}
}
export const tryCatchSync = <T>(fn: () => T): Result<T, ErrorInfo> => {
try {
const data = fn()
return success(data)
} catch (error) {
return failure(createErrorInfo(error))
}
}
export const map = <T, U, E>(result: Result<T, E>, fn: (data: T) => U): Result<U, E> => {
if (isSuccess(result)) {
return tryCatchSync(() => fn(result.data)) as Result<U, E>
}
return result
}
export const andThen = async <T, U, E>(
result: Result<T, E>,
fn: (data: T) => Promise<Result<U, E>> | Result<U, E>
): Promise<Result<U, E>> => {
if (isSuccess(result)) {
try {
return await fn(result.data)
} catch (error) {
return failure(createErrorInfo(error) as E)
}
}
return result
}
export const andThenSync = <T, U, E>(
result: Result<T, E>,
fn: (data: T) => Result<U, E>
): Result<U, E> => {
if (isSuccess(result)) {
try {
return fn(result.data)
} catch (error) {
return failure(createErrorInfo(error) as E)
}
}
return result
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment