diff --git a/packages/react-native/Libraries/Animated/animations/Animation.js b/packages/react-native/Libraries/Animated/animations/Animation.js index b071a89661b1f1..238958f14900ed 100644 --- a/packages/react-native/Libraries/Animated/animations/Animation.js +++ b/packages/react-native/Libraries/Animated/animations/Animation.js @@ -168,7 +168,7 @@ export default class Animation { ); return true; - } catch (e) { + } catch (e: unknown) { throw e; } finally { NativeAnimatedHelper.API.unsetWaitingForIdentifier( diff --git a/packages/react-native/Libraries/BatchedBridge/MessageQueue.js b/packages/react-native/Libraries/BatchedBridge/MessageQueue.js index fe557ed59c1485..4f18c0686c28f2 100644 --- a/packages/react-native/Libraries/BatchedBridge/MessageQueue.js +++ b/packages/react-native/Libraries/BatchedBridge/MessageQueue.js @@ -372,7 +372,7 @@ class MessageQueue { } else { try { fn(); - } catch (error) { + } catch (error: unknown) { ErrorUtils.reportFatalError(error); } } diff --git a/packages/react-native/Libraries/Core/Timers/JSTimers.js b/packages/react-native/Libraries/Core/Timers/JSTimers.js index 4ef450e0bc139e..b0b57dfbc23b7d 100644 --- a/packages/react-native/Libraries/Core/Timers/JSTimers.js +++ b/packages/react-native/Libraries/Core/Timers/JSTimers.js @@ -11,6 +11,7 @@ import NativeTiming from './NativeTiming'; +const toError = require('../../../src/private/utilities/toError').default; const BatchedBridge = require('../../BatchedBridge/BatchedBridge').default; const Systrace = require('../../Performance/Systrace'); const invariant = require('invariant'); @@ -129,9 +130,9 @@ function _callTimer(timerID: number, frameTime: number, didTimeout: ?boolean) { } else { console.error('Tried to call a callback with invalid type: ' + type); } - } catch (e) { + } catch (e: unknown) { // Don't rethrow so that we can run all timers. - errors.push(e); + errors.push(toError(e)); } if (__DEV__) { diff --git a/packages/react-native/Libraries/Core/Timers/__tests__/JSTimers-test.js b/packages/react-native/Libraries/Core/Timers/__tests__/JSTimers-test.js index 3619fa53d382f4..0f5ebec5ad492d 100644 --- a/packages/react-native/Libraries/Core/Timers/__tests__/JSTimers-test.js +++ b/packages/react-native/Libraries/Core/Timers/__tests__/JSTimers-test.js @@ -16,13 +16,10 @@ const NativeTiming = { setSendIdleEvents: jest.fn(), }; -jest - .enableAutomock() - .mock('../NativeTiming', () => ({ - __esModule: true, - default: NativeTiming, - })) - .unmock('../JSTimers'); +jest.mock('../NativeTiming', () => ({ + __esModule: true, + default: NativeTiming, +})); const JSTimers = require('../JSTimers').default; diff --git a/packages/react-native/Libraries/Core/setUpErrorHandling.js b/packages/react-native/Libraries/Core/setUpErrorHandling.js index f302c89d83339b..357c12e3bf7989 100644 --- a/packages/react-native/Libraries/Core/setUpErrorHandling.js +++ b/packages/react-native/Libraries/Core/setUpErrorHandling.js @@ -16,6 +16,7 @@ if (global.RN$useAlwaysAvailableJSErrorHandling !== true) { * You can use this module directly, or just require InitializeCore. */ const ExceptionsManager = require('./ExceptionsManager').default; + const toError = require('../../src/private/utilities/toError').default; ExceptionsManager.installConsoleErrorReporter(); // Set up error handler @@ -23,8 +24,9 @@ if (global.RN$useAlwaysAvailableJSErrorHandling !== true) { const handleError = (e: unknown, isFatal: boolean) => { try { ExceptionsManager.handleException(e, isFatal); - } catch (ee) { - console.log('Failed to print error: ', ee.message); + } catch (ee: unknown) { + const error = toError(ee); + console.log('Failed to print error: ', error.message); throw e; } }; diff --git a/packages/react-native/Libraries/Core/setUpReactDevTools.js b/packages/react-native/Libraries/Core/setUpReactDevTools.js index 9bdf66b30184b9..d5ed254acdff1e 100644 --- a/packages/react-native/Libraries/Core/setUpReactDevTools.js +++ b/packages/react-native/Libraries/Core/setUpReactDevTools.js @@ -49,7 +49,7 @@ if (__DEV__) { try { const parsedSettings = JSON.parse(serializedHookSettings); hookSettings = parsedSettings; - } catch { + } catch (e: unknown) { console.error( 'Failed to parse persisted React DevTools hook settings. React DevTools will be initialized with default settings.', ); diff --git a/packages/react-native/Libraries/Interaction/InteractionManager.js b/packages/react-native/Libraries/Interaction/InteractionManager.js index 81aae40474da9a..55c4c27e85b0c6 100644 --- a/packages/react-native/Libraries/Interaction/InteractionManager.js +++ b/packages/react-native/Libraries/Interaction/InteractionManager.js @@ -10,6 +10,7 @@ import type {EventSubscription} from '../vendor/emitter/EventEmitter'; +const toError = require('../../src/private/utilities/toError').default; const invariant = require('invariant'); export type SimpleTask = { @@ -113,8 +114,8 @@ const InteractionManagerStub = { try { task.run(); resolve(); - } catch (error) { - reject(error); + } catch (error: unknown) { + reject(toError(error)); } } else { reject(new TypeError(`Task "${task.name}" missing gen or run.`)); @@ -123,8 +124,8 @@ const InteractionManagerStub = { try { task(); resolve(); - } catch (error) { - reject(error); + } catch (error: unknown) { + reject(toError(error)); } } else { reject(new TypeError('Invalid task of type: ' + typeof task)); diff --git a/packages/react-native/Libraries/LogBox/Data/LogBoxData.js b/packages/react-native/Libraries/LogBox/Data/LogBoxData.js index a874864a746af4..277a989a765690 100644 --- a/packages/react-native/Libraries/LogBox/Data/LogBoxData.js +++ b/packages/react-native/Libraries/LogBox/Data/LogBoxData.js @@ -19,6 +19,7 @@ import type { } from './parseLogBoxLog'; import DebuggerSessionObserver from '../../../src/private/devsupport/rndevtools/FuseboxSessionObserver'; +import toExtendedError from '../../../src/private/utilities/toExtendedError'; import parseErrorStack from '../../Core/Devtools/parseErrorStack'; import NativeLogBox from '../../NativeModules/specs/NativeLogBox'; import LogBoxLog from './LogBoxLog'; @@ -240,8 +241,8 @@ export function addLog(log: LogData): void { componentStackType: log.componentStackType || 'legacy', }), ); - } catch (error) { - reportLogBoxError(error); + } catch (error: unknown) { + reportLogBoxError(toExtendedError(error)); } }); } @@ -252,8 +253,8 @@ export function addException(error: ExtendedExceptionData): void { setImmediate(() => { try { appendNewLog(new LogBoxLog(parseLogBoxException(error))); - } catch (loggingError) { - reportLogBoxError(loggingError); + } catch (loggingError: unknown) { + reportLogBoxError(toExtendedError(loggingError)); } }); } diff --git a/packages/react-native/Libraries/LogBox/LogBox.js b/packages/react-native/Libraries/LogBox/LogBox.js index e966de1cb034c8..96ff3ad64b8fbd 100644 --- a/packages/react-native/Libraries/LogBox/LogBox.js +++ b/packages/react-native/Libraries/LogBox/LogBox.js @@ -11,6 +11,7 @@ import type {IgnorePattern, LogData} from './Data/LogBoxData'; import type {ExtendedExceptionData} from './Data/parseLogBoxLog'; +import toExtendedError from '../../src/private/utilities/toExtendedError'; import Platform from '../Utilities/Platform'; import RCTLog from '../Utilities/RCTLog'; import * as React from 'react'; @@ -192,8 +193,8 @@ if (__DEV__) { componentStackType, }); } - } catch (err) { - LogBoxData.reportLogBoxError(err); + } catch (err: unknown) { + LogBoxData.reportLogBoxError(toExtendedError(err)); } } }, @@ -237,8 +238,8 @@ if (__DEV__) { }); } } - } catch (err) { - LogBoxData.reportLogBoxError(err); + } catch (err: unknown) { + LogBoxData.reportLogBoxError(toExtendedError(err)); } }; } else { diff --git a/packages/react-native/Libraries/Network/XMLHttpRequest.js b/packages/react-native/Libraries/Network/XMLHttpRequest.js index be83740f65ae9a..d097f8a79747e2 100644 --- a/packages/react-native/Libraries/Network/XMLHttpRequest.js +++ b/packages/react-native/Libraries/Network/XMLHttpRequest.js @@ -284,7 +284,7 @@ class XMLHttpRequest extends EventTarget { case 'json': try { this._cachedResponse = JSON.parse(this._response); - } catch (_) { + } catch (_: unknown) { this._cachedResponse = null; } break; diff --git a/packages/react-native/src/private/utilities/toError.js b/packages/react-native/src/private/utilities/toError.js new file mode 100644 index 00000000000000..fa1c3c9863cbf4 --- /dev/null +++ b/packages/react-native/src/private/utilities/toError.js @@ -0,0 +1,27 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +/** + * Converts an unknown value to an Error instance. + * If the value is already an Error, returns it as-is. + * Otherwise, creates a new Error with the stringified value as the message. + * + * This is particularly useful in catch blocks where the caught value + * is annotated as `unknown` but needs to be treated as an Error. + * + * @param value - The unknown value to convert to an Error + * @returns An Error instance + */ +export default function toError(value: unknown): Error { + if (value instanceof Error) { + return value; + } + return new Error(String(value)); +} diff --git a/packages/react-native/src/private/utilities/toExtendedError.js b/packages/react-native/src/private/utilities/toExtendedError.js new file mode 100644 index 00000000000000..988a72796f4586 --- /dev/null +++ b/packages/react-native/src/private/utilities/toExtendedError.js @@ -0,0 +1,30 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +import type {ExtendedError} from '../../../Libraries/Core/ExtendedError'; + +import toError from './toError'; + +/** + * Converts an unknown value to an ExtendedError instance suitable for LogBox. + * Uses the standard toError utility internally and then casts to ExtendedError. + * + * This is specifically designed for LogBox error reporting which requires + * ExtendedError type compatibility. + * + * @param value - The unknown value to convert to an ExtendedError + * @returns An ExtendedError instance + */ +export default function toExtendedError(value: unknown): ExtendedError { + const error = toError(value); + // ExtendedError extends Error, so this cast is safe for the LogBox system + // $FlowFixMe[incompatible-type] ExtendedError extends Error, this cast is safe + return (error: ExtendedError); +}