diff --git a/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp b/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp index 5a33246720a06d..85e5d3d9268f78 100644 --- a/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp +++ b/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp @@ -134,7 +134,11 @@ TimerHandle TimerManager::createRecurringTimer( void TimerManager::deleteTimer(jsi::Runtime& runtime, TimerHandle timerHandle) { if (timerHandle < 0) { - throw jsi::JSError(runtime, "clearTimeout called with an invalid handle"); + // Do nothing for invalid handles (match web spec behavior) + // Links: + // - cancelAnimationFrame: https://www.w3.org/TR/animation-timing/; + // - clearTimeout: https://developer.mozilla.org/en-US/docs/Web/API/Window/clearTimeout#notes) + return; } platformTimerRegistry_->deleteTimer(timerHandle); @@ -145,7 +149,10 @@ void TimerManager::deleteRecurringTimer( jsi::Runtime& runtime, TimerHandle timerHandle) { if (timerHandle < 0) { - throw jsi::JSError(runtime, "clearInterval called with an invalid handle"); + // Do nothing for invalid handles (match web spec behavior) + // Links: + // - clearInterval: https://developer.mozilla.org/en-US/docs/Web/API/Window/clearInterval + return; } platformTimerRegistry_->deleteTimer(timerHandle); diff --git a/packages/react-native/src/types/globals.d.ts b/packages/react-native/src/types/globals.d.ts index 34d46cc3fecb09..4082da3528042a 100644 --- a/packages/react-native/src/types/globals.d.ts +++ b/packages/react-native/src/types/globals.d.ts @@ -101,8 +101,8 @@ declare global { // #region Timer Functions - function clearInterval(handle: number): void; - function clearTimeout(handle: number): void; + function clearInterval(handle: number | null | undefined): void; + function clearTimeout(handle: number | null | undefined): void; function setInterval(handler: () => void, timeout: number): number; function setInterval( handler: (...args: Args) => void, @@ -115,14 +115,14 @@ declare global { timeout?: number, ...args: Args ): number; - function clearImmediate(handle: number): void; + function clearImmediate(handle: number | null | undefined): void; function setImmediate(handler: () => void): number; function setImmediate( handler: (...args: Args) => void, ...args: Args ): number; - function cancelAnimationFrame(handle: number): void; + function cancelAnimationFrame(handle: number | null | undefined): void; function requestAnimationFrame(callback: (time: number) => void): number; function fetchBundle( diff --git a/packages/react-native/types/__typetests__/globals.tsx b/packages/react-native/types/__typetests__/globals.tsx index 70d8b33b17afa7..8bacebf8c5f733 100644 --- a/packages/react-native/types/__typetests__/globals.tsx +++ b/packages/react-native/types/__typetests__/globals.tsx @@ -8,6 +8,9 @@ const noop = () => { }; function testInterval() { + clearInterval(null); + clearInterval(undefined); + let handle = setInterval(noop, 0); clearInterval(handle); @@ -38,6 +41,9 @@ function testInterval() { } function testTimeout() { + clearTimeout(null); + clearTimeout(undefined); + let handle = setTimeout(noop, 0); clearTimeout(handle); @@ -68,6 +74,9 @@ function testTimeout() { } function testImmediate() { + clearImmediate(null); + clearImmediate(undefined); + let handle = setImmediate(noop); clearImmediate(handle); @@ -97,6 +106,37 @@ function testImmediate() { clearImmediate(handle); } +function testRequestAnimationFrame(){ + cancelAnimationFrame(null); + cancelAnimationFrame(undefined); + + let handle = requestAnimationFrame((time: number) => { + console.log('time', time); + }); + cancelAnimationFrame(handle); + + handle = requestAnimationFrame(() => { + console.log('no time'); + }); + cancelAnimationFrame(handle); + + handle = requestAnimationFrame( + // @ts-expect-error + (notTime: string) => { + console.log('argument have to be number', notTime); + }); + cancelAnimationFrame(handle); + + // @ts-expect-error + const resultHaveToBeNum: string = requestAnimationFrame(() => { + console.log('result have to be number'); + }); + cancelAnimationFrame( + // @ts-expect-error + resultHaveToBeNum + ); +} + const fetchCopy: WindowOrWorkerGlobalScope['fetch'] = fetch; const myHeaders = new Headers();