Skip to content

Commit 9aea72a

Browse files
authored
Fix error type (Error -> unknown) (#235)
1 parent 186ae40 commit 9aea72a

23 files changed

Lines changed: 102 additions & 35 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ None
6363
<tr>
6464
<td>onError</td>
6565
<td><p>Optional callback to enable e.g. logging error information to a server.
66-
@param error Error that was thrown
66+
@param error Value that was thrown; typically an instance of <code>Error</code>
6767
@param info React &quot;component stack&quot; identifying where the error was thrown</p>
6868
</td>
6969
</tr>

lib/components/ErrorBoundary.test.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
FallbackProps,
1717
OnErrorCallback,
1818
} from "../types";
19+
import { getErrorMessage } from "../utils/getErrorMessage";
1920

2021
describe("ErrorBoundary", () => {
2122
let container: HTMLDivElement;
@@ -84,7 +85,7 @@ describe("ErrorBoundary", () => {
8485
render({ onError });
8586

8687
expect(onError).toHaveBeenCalledTimes(1);
87-
expect(onError.mock.calls[0][0].message).toEqual("💥💥💥");
88+
expect(getErrorMessage(onError.mock.calls[0][0])).toEqual("💥💥💥");
8889
});
8990

9091
it('should call "onReset" when boundary reset via imperative API', () => {
@@ -169,7 +170,7 @@ describe("ErrorBoundary", () => {
169170

170171
describe('"FallbackComponent"', () => {
171172
let fallbackComponent: Mock<(props: FallbackProps) => ReactElement>;
172-
let lastRenderedError: Error | null = null;
173+
let lastRenderedError: unknown | null = null;
173174
let lastRenderedResetErrorBoundary:
174175
| FallbackProps["resetErrorBoundary"]
175176
| null = null;
@@ -204,7 +205,7 @@ describe("ErrorBoundary", () => {
204205
it("should render fallback in the event of an error", () => {
205206
shouldThrow = true;
206207
render();
207-
expect(lastRenderedError?.message).toBe("💥💥💥");
208+
expect(getErrorMessage(lastRenderedError)).toBe("💥💥💥");
208209
expect(container.textContent).toBe("FallbackComponent");
209210
});
210211

@@ -235,7 +236,7 @@ describe("ErrorBoundary", () => {
235236
});
236237

237238
describe('"fallbackRender" render prop', () => {
238-
let lastRenderedError: Error | null = null;
239+
let lastRenderedError: unknown | null = null;
239240
let lastRenderedResetErrorBoundary:
240241
| FallbackProps["resetErrorBoundary"]
241242
| null = null;
@@ -271,15 +272,15 @@ describe("ErrorBoundary", () => {
271272
it("should render fallback in the event of an error", () => {
272273
shouldThrow = true;
273274
render();
274-
expect(lastRenderedError?.message).toBe("💥💥💥");
275+
expect(getErrorMessage(lastRenderedError)).toBe("💥💥💥");
275276
expect(fallbackRender).toHaveBeenCalled();
276277
expect(container.textContent).toBe("fallbackRender");
277278
});
278279

279280
it("should re-render children if boundary is reset via prop", () => {
280281
shouldThrow = true;
281282
render();
282-
expect(lastRenderedError?.message).toBe("💥💥💥");
283+
expect(getErrorMessage(lastRenderedError)).toBe("💥💥💥");
283284
expect(fallbackRender).toHaveBeenCalled();
284285
expect(container.textContent).toBe("fallbackRender");
285286

@@ -295,7 +296,7 @@ describe("ErrorBoundary", () => {
295296
it("should re-render children if boundary is reset reset keys", () => {
296297
shouldThrow = true;
297298
render({ resetKeys: [1] });
298-
expect(lastRenderedError?.message).toBe("💥💥💥");
299+
expect(getErrorMessage(lastRenderedError)).toBe("💥💥💥");
299300
expect(fallbackRender).toHaveBeenCalled();
300301
expect(container.textContent).toBe("fallbackRender");
301302

@@ -306,7 +307,7 @@ describe("ErrorBoundary", () => {
306307
});
307308

308309
describe("thrown values", () => {
309-
let lastRenderedError: Error | null = null;
310+
let lastRenderedError: unknown | null = null;
310311
let fallbackRender: (props: FallbackProps) => ReactElement;
311312
let onError: Mock<(...args: unknown[]) => unknown>;
312313

lib/components/ErrorBoundary.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const isDevelopment = import.meta.env.DEV;
77
type ErrorBoundaryState =
88
| {
99
didCatch: true;
10-
error: Error;
10+
error: unknown;
1111
}
1212
| {
1313
didCatch: false;
@@ -63,7 +63,7 @@ export class ErrorBoundary extends Component<
6363
}
6464
}
6565

66-
componentDidCatch(error: Error, info: ErrorInfo) {
66+
componentDidCatch(error: unknown, info: ErrorInfo) {
6767
this.props.onError?.(error, info);
6868
}
6969

lib/context/ErrorBoundaryContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createContext } from "react";
22

33
export type ErrorBoundaryContextType = {
44
didCatch: boolean;
5-
error: Error | null;
5+
error: unknown | null;
66
resetErrorBoundary: (...args: unknown[]) => void;
77
};
88

lib/hooks/useErrorBoundary.test.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createRoot } from "react-dom/client";
33
import { beforeEach, describe, expect, it, vi } from "vitest";
44
import { ErrorBoundary } from "../components/ErrorBoundary";
55
import { assert } from "../utils/assert";
6+
import { getErrorMessage } from "../utils/getErrorMessage";
67
import { useErrorBoundary, type UseErrorBoundaryApi } from "./useErrorBoundary";
78

89
describe("useErrorBoundary", () => {
@@ -41,7 +42,9 @@ describe("useErrorBoundary", () => {
4142

4243
render(
4344
<ErrorBoundary
44-
fallbackRender={({ error }) => <div>Fallback: {error.message}</div>}
45+
fallbackRender={({ error }) => (
46+
<div>Fallback: {getErrorMessage(error)}</div>
47+
)}
4548
>
4649
<Child />
4750
</ErrorBoundary>,

lib/hooks/useErrorBoundary.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { ErrorBoundaryContext } from "../context/ErrorBoundaryContext";
33
import { assertErrorBoundaryContext } from "../utils/assertErrorBoundaryContext";
44

55
type UseErrorBoundaryState =
6-
| { error: Error; hasError: true }
6+
| { error: unknown; hasError: true }
77
| { error: null; hasError: false };
88

99
export type UseErrorBoundaryApi = {
10-
error: Error | null;
10+
error: unknown | null;
1111
resetBoundary: () => void;
12-
showBoundary: (error: Error) => void;
12+
showBoundary: (error: unknown) => void;
1313
};
1414

1515
/**
@@ -21,7 +21,7 @@ export function useErrorBoundary(): {
2121
/**
2222
* The currently visible `Error` (if one has been thrown).
2323
*/
24-
error: Error | null;
24+
error: unknown | null;
2525

2626
/**
2727
* Method to reset and retry the nearest active error boundary (if one is active).
@@ -35,7 +35,7 @@ export function useErrorBoundary(): {
3535
* Errors thrown in event handlers, or after async code has run, will not be caught.
3636
* This method is a way to imperatively trigger an error boundary during these phases.
3737
*/
38-
showBoundary: (error: Error) => void;
38+
showBoundary: (error: unknown) => void;
3939
} {
4040
const context = useContext(ErrorBoundaryContext);
4141

@@ -55,7 +55,7 @@ export function useErrorBoundary(): {
5555
resetErrorBoundary();
5656
setState({ error: null, hasError: false });
5757
},
58-
showBoundary: (error: Error) =>
58+
showBoundary: (error: unknown) =>
5959
setState({
6060
error,
6161
hasError: true,

lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
export { ErrorBoundary } from "./components/ErrorBoundary";
44
export { ErrorBoundaryContext } from "./context/ErrorBoundaryContext";
55
export { useErrorBoundary } from "./hooks/useErrorBoundary";
6+
export { getErrorMessage } from "./utils/getErrorMessage";
67
export { withErrorBoundary } from "./utils/withErrorBoundary";
78

89
export type { ErrorBoundaryContextType } from "./context/ErrorBoundaryContext";

lib/types.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@ import type {
66
} from "react";
77

88
export type FallbackProps = {
9-
error: Error;
9+
error: unknown;
1010
resetErrorBoundary: (...args: unknown[]) => void;
1111
};
1212

13-
export type OnErrorCallback = (error: Error, info: ErrorInfo) => void;
13+
export type OnErrorCallback = (error: unknown, info: ErrorInfo) => void;
1414

1515
type ErrorBoundarySharedProps = PropsWithChildren<{
1616
/**
1717
* Optional callback to enable e.g. logging error information to a server.
1818
*
19-
* @param error Error that was thrown
19+
* @param error Value that was thrown; typically an instance of `Error`
2020
* @param info React "component stack" identifying where the error was thrown
2121
*/
22-
onError?: (error: Error, info: ErrorInfo) => void;
22+
onError?: (error: unknown, info: ErrorInfo) => void;
2323

2424
/**
2525
* Optional callback to to be notified when an error boundary is "reset" so React can retry the failed render.

lib/utils/getErrorMessage.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export function getErrorMessage(thrown: unknown): string | undefined {
2+
switch (typeof thrown) {
3+
case "object": {
4+
if (
5+
thrown !== null &&
6+
"message" in thrown &&
7+
typeof thrown.message === "string"
8+
) {
9+
return thrown.message;
10+
}
11+
break;
12+
}
13+
case "string": {
14+
return thrown;
15+
}
16+
}
17+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-error-boundary",
3-
"version": "6.0.3",
3+
"version": "6.0.4",
44
"type": "module",
55
"description": "Simple reusable React error boundary component",
66
"author": "Brian Vaughn <brian.david.vaughn@gmail.com>",

0 commit comments

Comments
 (0)