From 4733a943fd0b9c63ae44a89a9a871db297faa5ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:33:21 +0000 Subject: [PATCH 1/2] Initial plan From ce99eb8c01d0d0914ba8074c695c31bf15e0a30c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:45:16 +0000 Subject: [PATCH 2/2] Add errorBoundaryProps utility function Co-authored-by: manudeli <61593290+manudeli@users.noreply.github.com> --- packages/react/src/ErrorBoundary.spec.tsx | 73 +++++++++++++++++++ packages/react/src/ErrorBoundary.test-d.tsx | 80 +++++++++++++++++++++ packages/react/src/ErrorBoundary.tsx | 20 ++++++ packages/react/src/index.ts | 2 +- 4 files changed, 174 insertions(+), 1 deletion(-) diff --git a/packages/react/src/ErrorBoundary.spec.tsx b/packages/react/src/ErrorBoundary.spec.tsx index 3ea52a297..b0738ee10 100644 --- a/packages/react/src/ErrorBoundary.spec.tsx +++ b/packages/react/src/ErrorBoundary.spec.tsx @@ -5,6 +5,7 @@ import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary' import { ErrorBoundary, type ErrorBoundaryFallbackProps, + errorBoundaryProps, useErrorBoundary, useErrorBoundaryFallbackProps, } from './ErrorBoundary' @@ -621,3 +622,75 @@ describe('ErrorBoundary.with', () => { expect(ErrorBoundary.with({ fallback: () => <> }, () => <>).displayName).toBe('ErrorBoundary.with(Component)') }) }) + +describe('errorBoundaryProps', () => { + beforeEach(() => vi.useFakeTimers()) + + afterEach(() => { + vi.useRealTimers() + Throw.reset() + }) + + it('should return the props object passed to it', () => { + const props = errorBoundaryProps({ + fallback:
Error
, + onError: vi.fn(), + onReset: vi.fn(), + }) + + expect(props).toEqual({ + fallback:
Error
, + onError: expect.any(Function), + onReset: expect.any(Function), + }) + }) + + it('should work with function fallback', () => { + const fallbackComponent = ({ error }: ErrorBoundaryFallbackProps) =>
{error.message}
+ const props = errorBoundaryProps({ + fallback: fallbackComponent, + }) + + expect(props.fallback).toBe(fallbackComponent) + }) + + it('should preserve type inference for shouldCatch', () => { + const props = errorBoundaryProps({ + fallback:
Error
, + shouldCatch: CustomError, + }) + + expect(props.shouldCatch).toBe(CustomError) + }) + + it('should work with resetKeys', () => { + const resetKeys = ['key1', 'key2'] + const props = errorBoundaryProps({ + fallback:
Error
, + resetKeys, + }) + + expect(props.resetKeys).toEqual(resetKeys) + }) + + it('should integrate with ErrorBoundary component', async () => { + const onError = vi.fn() + const props = errorBoundaryProps({ + fallback: (props) => <>{props.error.message}, + onError, + }) + + render( + + + {TEXT} + + + ) + + expect(screen.queryByText(TEXT)).toBeInTheDocument() + await act(() => vi.advanceTimersByTime(100)) + expect(screen.queryByText(ERROR_MESSAGE)).toBeInTheDocument() + expect(onError).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/react/src/ErrorBoundary.test-d.tsx b/packages/react/src/ErrorBoundary.test-d.tsx index c7b623110..bb853e76f 100644 --- a/packages/react/src/ErrorBoundary.test-d.tsx +++ b/packages/react/src/ErrorBoundary.test-d.tsx @@ -1231,3 +1231,83 @@ describe('', () => { }) }) }) + +describe('errorBoundaryProps', () => { + it('should return props with correct types', () => { + const { errorBoundaryProps } = require('./ErrorBoundary') + + const props = errorBoundaryProps({ + fallback:
Error
, + }) + + expectTypeOf(props).toMatchTypeOf<{ + fallback: ReactNode + }>() + }) + + it('should preserve type inference for shouldCatch with CustomError', () => { + const { errorBoundaryProps } = require('./ErrorBoundary') + + const props = errorBoundaryProps({ + fallback: ({ error }: { error: CustomError; reset: () => void }) =>
{error.message}
, + shouldCatch: CustomError, + }) + + expectTypeOf(props).toMatchTypeOf<{ + fallback: ReactNode + shouldCatch?: typeof CustomError + }>() + }) + + it('should work with function fallback that has typed error', () => { + const { errorBoundaryProps } = require('./ErrorBoundary') + + const props = errorBoundaryProps({ + fallback: ({ error, reset }: { error: CustomError; reset: () => void }) => ( +
+ {error.message} + +
+ ), + shouldCatch: CustomError, + }) + + expectTypeOf(props.fallback).toMatchTypeOf< + ReactNode | ((props: { error: CustomError; reset: () => void }) => ReactNode) + >() + }) + + it('should accept all ErrorBoundary props except children', () => { + const { errorBoundaryProps } = require('./ErrorBoundary') + + const props = errorBoundaryProps({ + fallback:
Error
, + resetKeys: ['key1', 'key2'], + onError: (error: CustomError) => console.error(error), + onReset: () => console.log('reset'), + shouldCatch: CustomError, + }) + + expectTypeOf(props).toMatchTypeOf<{ + fallback: ReactNode + resetKeys?: unknown[] + onError?: (error: CustomError, info: unknown) => void + onReset?: () => void + shouldCatch?: typeof CustomError + }>() + }) + + it('should work with array of error matchers', () => { + const { errorBoundaryProps } = require('./ErrorBoundary') + + const props = errorBoundaryProps({ + fallback: ({ error }: { error: Error; reset: () => void }) =>
{error.message}
, + shouldCatch: [CustomError, (error: Error) => error instanceof Error], + }) + + expectTypeOf(props).toMatchTypeOf<{ + fallback: ReactNode + shouldCatch?: [typeof CustomError, (error: Error) => boolean] + }>() + }) +}) diff --git a/packages/react/src/ErrorBoundary.tsx b/packages/react/src/ErrorBoundary.tsx index a93683c9f..33d1b4f08 100644 --- a/packages/react/src/ErrorBoundary.tsx +++ b/packages/react/src/ErrorBoundary.tsx @@ -319,3 +319,23 @@ export const useErrorBoundaryFallbackProps = (): E [errorBoundary.error, errorBoundary.reset] ) } + +/** + * This utility function is a helper for creating ErrorBoundary props with proper type inference. + * It simply returns the props object passed to it, but helps with type inference. + * @see {@link https://suspensive.org/docs/react/ErrorBoundary Suspensive Docs} + * @example + * ```tsx + * const errorBoundary = errorBoundaryProps({ + * fallback: ({ error }) =>
{error.message}
, + * onError: (error) => console.error(error), + * }) + * + * + * + * + * ``` + */ +export const errorBoundaryProps = ( + props: PropsWithoutChildren> +) => props diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index f391000b4..cf1f51ff3 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,7 +1,7 @@ export { ClientOnly } from './ClientOnly' export { DefaultPropsProvider, DefaultProps } from './DefaultProps' export { Delay } from './Delay' -export { ErrorBoundary, useErrorBoundary, useErrorBoundaryFallbackProps } from './ErrorBoundary' +export { ErrorBoundary, useErrorBoundary, useErrorBoundaryFallbackProps, errorBoundaryProps } from './ErrorBoundary' export { ErrorBoundaryGroup, useErrorBoundaryGroup } from './ErrorBoundaryGroup' export { lazy, reloadOnError, createLazy } from './lazy' export { Suspense } from './Suspense'