DEV Community

Na'aman Hirschfeld
Na'aman Hirschfeld

Posted on

Functional Snippet: withSuppress

Sometimes, suppressing errors is handy.

E.g. you see this:

try { scrollableRef.update() } catch {} 
Enter fullscreen mode Exit fullscreen mode

Let's create something to handle this in a cleaner fashion. We'll use a functional style that prefers wrap functions to extend functionality.

We "wrap" a function with error handling, returning a function with an identical signature:

import { isPromise } from '@tool-belt/type-predicates'; /** * Wraps a function with error suppression and optional logging. * * @param fn - The function to execute. * @param errorMessage - The error message to log if an exception is thrown. * @returns The wrapped function with error suppression. */ export function withSuppress<T extends (...args: any[]) => any>( fn: T, errorMessage?: string, ): (...args: Parameters<T>) => ReturnType<T> extends Promise<infer U> ? Promise<U | undefined> : ReturnType<T> | undefined { const log = errorMessage ? (msg: string) => console.error(msg) : null; return (...args: Parameters<T>) => { try { const result = fn(...args); if (isPromise(result)) { return result.catch((error) => { log?.(`${errorMessage}\n${error?.stack ?? error}`); return undefined; }) as ReturnType<T> extends Promise<infer U> ? Promise<U | undefined> : never; } return result; } catch (error) { log?.(`${errorMessage}\n${error?.stack ?? error}`); return undefined; } }; } 
Enter fullscreen mode Exit fullscreen mode

And now we can suppress errors elegantly.

Here are some example uses:

# generate content 
Enter fullscreen mode Exit fullscreen mode

Here is a Vitest test suite for the function:

import { withSuppress } from './functional'; describe('withSuppress', () => { it('should return the value from a synchronous function without errors', () => { const syncFn = () => 42; const wrappedFn = withSuppress(syncFn); expect(wrappedFn()).toBe(42); }); it('should return undefined and log error when synchronous function throws', () => { const errorMessage = 'Sync error occurred'; const syncFn = () => { throw new Error('Test Error'); }; const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const wrappedFn = withSuppress(syncFn, errorMessage); expect(wrappedFn()).toBeUndefined(); expect(consoleErrorSpy).toHaveBeenCalledWith( expect.stringContaining(`${errorMessage}\nError: Test Error`), ); consoleErrorSpy.mockRestore(); }); it('should return the resolved value from an asynchronous function without errors', async () => { const asyncFn = async () => await Promise.resolve(42); const wrappedFn = withSuppress(asyncFn); await expect(wrappedFn()).resolves.toBe(42); }); it('should return undefined and log error when asynchronous function rejects', async () => { const errorMessage = 'Async error occurred'; const asyncFn = async () => await Promise.reject(new Error('Test Error')); const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const wrappedFn = withSuppress(asyncFn, errorMessage); await expect(wrappedFn()).resolves.toBeUndefined(); expect(consoleErrorSpy).toHaveBeenCalledWith( expect.stringContaining(`${errorMessage}\nError: Test Error`), ); consoleErrorSpy.mockRestore(); }); it('should return undefined if no error message is provided and the function throws', () => { const syncFn = () => { throw new Error('Test Error'); }; const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const wrappedFn = withSuppress(syncFn); expect(wrappedFn()).toBeUndefined(); expect(consoleErrorSpy).not.toHaveBeenCalled(); consoleErrorSpy.mockRestore(); }); it('should return undefined if no error message is provided and the async function rejects', async () => { const asyncFn = async () => await Promise.reject(new Error('Test Error')); const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const wrappedFn = withSuppress(asyncFn); await expect(wrappedFn()).resolves.toBeUndefined(); expect(consoleErrorSpy).not.toHaveBeenCalled(); consoleErrorSpy.mockRestore(); }); it('should handle functions that return promises without errors', async () => { const asyncFn = async () => await Promise.resolve('async result'); const wrappedFn = withSuppress(asyncFn); await expect(wrappedFn()).resolves.toBe('async result'); }); it('should handle synchronous functions returning undefined without errors', () => { const syncFn = () => undefined; const wrappedFn = withSuppress(syncFn); expect(wrappedFn()).toBeUndefined(); }); }); 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)