-
Star
(746)
You must be signed in to star a gist -
Fork
(118)
You must be signed in to fork a gist
-
-
Save t3dotgg/a486c4ae66d32bf17c09c73609dacc5b to your computer and use it in GitHub Desktop.
// Types for the result object with discriminated union | |
type Success<T> = { | |
data: T; | |
error: null; | |
}; | |
type Failure<E> = { | |
data: null; | |
error: E; | |
}; | |
type Result<T, E = Error> = Success<T> | Failure<E>; | |
// Main wrapper function | |
export async function tryCatch<T, E = Error>( | |
promise: Promise<T>, | |
): Promise<Result<T, E>> { | |
try { | |
const data = await promise; | |
return { data, error: null }; | |
} catch (error) { | |
return { data: null, error: error as E }; | |
} | |
} |
i made a npm package which is basically just theo's version but with added support for sync functions
https://www.npmjs.com/package/typecatchThis would work better if it returned undefined on error, so you could use a default value shorthand when spreading an object.
Impossible, since you can only know that at runtime.
i made a npm package which is basically just theo's version but with added support for sync functions
npmjs.com/package/typecatchThis would work better if it returned undefined on error, so you could use a default value shorthand when spreading an object.
![]()
update to 0.2.2 :)
How do I make it work for passing in a arrow function with an await inside of it, for example:
onst { data: uploadInsertResult, error: uploadInsertError } = await tryCatch( async () => { const result = await archeWebDb .insert(uploads) .values({ uploadName: file.name, saveDirectory: env.UPLOADS_DIR, extension: extension, }) .$returningId(); // This line should not be followed by a comma return result; }, );type Success<T> = readonly [null, T] type Failure<E> = readonly [E, null] type ResultSync<T, E> = Success<T> | Failure<E> type ResultAsync<T, E> = Promise<ResultSync<T, E>> type Operation<T> = Promise<T> | (() => T) | (() => Promise<T>) export function tryCatch<T, E = Error>(operation: Promise<T>): ResultAsync<T, E> export function tryCatch<T, E = Error>(operation: () => Promise<T>): ResultAsync<T, E> export function tryCatch<T, E = Error>(operation: () => T): ResultSync<T, E> export function tryCatch<T, E = Error>(operation: Operation<T>): ResultSync<T, E> | ResultAsync<T, E> { if (operation instanceof Promise) { return operation.then((data: T) => [null, data] as const).catch((error: E) => [error as E, null] as const) } try { const result = operation() if (result instanceof Promise) { return result.then((data: T) => [null, data] as const).catch((error: E) => [error as E, null] as const) } return [null, result] as const } catch (error) { return [error as E, null] as const } } const [error, data] = await tryCatch(async () => { const [file] = await db .insert(filesTable) .values({ size: 1234, mimeType: "text/plain", path: "/uploads/test.txt" }) .returning() if (!file) { throw new Error("File creation failed") } return file }) if (error) { console.error("Error creating the file:", error) // Handle the error throw error } console.log(data)
I used the code above as a baseline and made some adjustments to better suit my needs. I also addressed a few additional edge cases—for example, adding an overload for the never type to prevent the Promise-specific overload from being incorrectly used. Additionally, I handled promises using Promise.resolve to ensure compatibility with external promises. This was particularly helpful in resolving issues I encountered when returning a PrismaPromise without awaiting it first.
Additionally, you could implement support for a second argument—such as a transformError function—to wrap or customize internal errors. This allows for more flexible error handling, especially when you want to add context or standardize error formats.
export type OperationSuccess<T> = readonly [data: T, error: null];
export type OperationFailure<E> = readonly [data: null, error: E];
export type OperationResult<T, E> = OperationSuccess<T> | OperationFailure<E>;
type Operation<T> = Promise<T> | (() => T) | (() => Promise<T>);
export function trycatch<T, E = Error>(operation: Promise<T>): Promise<OperationResult<T, E>>;
export function trycatch<T, E = Error>(operation: () => never): OperationResult<never, E>;
export function trycatch<T, E = Error>(operation: () => Promise<T>): Promise<OperationResult<T, E>>;
export function trycatch<T, E = Error>(operation: () => T): OperationResult<T, E>;
export function trycatch<T, E = Error>(
operation: Operation<T>,
): OperationResult<T, E> | Promise<OperationResult<T, E>> {
try {
const result = typeof operation === 'function' ? operation() : operation;
return Promise.resolve(result)
.then((data) => onSuccess(data))
.catch((error) => onFailure(error));
} catch (error) {
return onFailure<E>(error);
}
}
const onSuccess = <T>(value: T): OperationSuccess<T> => {
return [value, null];
};
const onFailure = <E>(error: unknown): OperationFailure<E> => {
const errorParsed = error instanceof Error ? error : new Error(String(error));
return [null, errorParsed as E];
};
// ---------------------------
// Testing
// ---------------------------
const main = async () => {
const syncCallback = () => 'data';
const asyncCallback = async () => 'data';
const promise = new Promise<string>((resolve) => resolve('data'));
const [syncCallbackResult, syncCallbackError] = trycatch(syncCallback);
const [asyncCallbackResult, asyncCallbackError] = await trycatch(asyncCallback);
const [promiseResult, promiseError] = await trycatch(promise);
};
i made a npm package which is basically just theo's version but with added support for sync functions
https://www.npmjs.com/package/typecatch