Skip to content

Instantly share code, notes, and snippets.

@bjornpagen
Last active April 25, 2025 02:39
Show Gist options
  • Select an option

  • Save bjornpagen/85e1efb8355c6f7e7a4e87bcaa4483a2 to your computer and use it in GitHub Desktop.

Select an option

Save bjornpagen/85e1efb8355c6f7e7a4e87bcaa4483a2 to your computer and use it in GitHub Desktop.
Good error handling in Typescript
export type Success<T> = {
data: T
error: undefined
}
export type Failure<E> = {
data: undefined
error: E
}
export type Result<T, E = Error> = Success<T> | Failure<E>
export async function tryCatch<T, E extends Error = Error>(
promise: Promise<T>
): Promise<Result<T, E>> {
try {
const data = await promise
return { data, error: undefined }
} catch (error) {
const finalError = error as E
return { data: undefined, error: finalError }
}
}
export function trySync<T, E extends Error = Error>(fn: () => T): Result<T, E> {
try {
const data = fn()
return { data, error: undefined }
} catch (error) {
const finalError = error as E
return { data: undefined, error: finalError }
}
}
export class WrappedError<E extends Error = Error> extends Error {
cause: E
constructor(message: string, cause: E) {
super(message)
this.name = "WrappedError"
this.cause = cause
if (Error.captureStackTrace) {
Error.captureStackTrace(this, WrappedError)
}
}
unwrap(): E {
return this.cause
}
toString(): string {
if (this.cause) {
return `${this.message}: ${this.cause.message}`
}
return this.message
}
static cause<T extends Error>(error: WrappedError<T>): T {
let result: Error = error.unwrap()
while (result instanceof WrappedError) {
result = result.unwrap()
}
return result as T
}
}
export function wrap<E extends Error>(
cause: E,
message: string
): WrappedError<E> {
return new WrappedError(message, cause)
}
export const Errors = Object.freeze({
try: tryCatch,
trySync,
wrap,
WrappedError
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment