From 1efbd32ccdb8cb4e25ceb39f47bb367443915316 Mon Sep 17 00:00:00 2001 From: D N <4661784+retyui@users.noreply.github.com> Date: Mon, 12 Jan 2026 18:54:00 +0100 Subject: [PATCH 1/3] chore: [TS] Allow to pass `null | undefined` types to cancelAnimationFrame, clearImmediate, clearTimeout, clearInterval --- packages/react-native/src/types/globals.d.ts | 8 ++-- .../types/__typetests__/globals.tsx | 40 +++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/packages/react-native/src/types/globals.d.ts b/packages/react-native/src/types/globals.d.ts index 34d46cc3fecb..4082da352804 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 70d8b33b17af..8bacebf8c5f7 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(); From 2c5fe40ca2cf84098978af99d2e7bf91ef4beec8 Mon Sep 17 00:00:00 2001 From: D N <4661784+retyui@users.noreply.github.com> Date: Mon, 12 Jan 2026 19:03:15 +0100 Subject: [PATCH 2/3] chore: Avoid throwing an error when a timer does not exist --- .../react-native/ReactCommon/react/runtime/TimerManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp b/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp index 5a33246720a0..5139e3c56f43 100644 --- a/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp +++ b/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp @@ -134,7 +134,8 @@ 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) + return; } platformTimerRegistry_->deleteTimer(timerHandle); From 025a8a62d58bc67fe6d2dfb82f6d9c72f04f8c42 Mon Sep 17 00:00:00 2001 From: D N <4661784+retyui@users.noreply.github.com> Date: Wed, 14 Jan 2026 11:15:24 +0100 Subject: [PATCH 3/3] chore: Add links to timers docs & update "deleteRecurringTimer" method --- .../ReactCommon/react/runtime/TimerManager.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp b/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp index 5139e3c56f43..85e5d3d9268f 100644 --- a/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp +++ b/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp @@ -135,6 +135,9 @@ TimerHandle TimerManager::createRecurringTimer( void TimerManager::deleteTimer(jsi::Runtime& runtime, TimerHandle timerHandle) { if (timerHandle < 0) { // 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; } @@ -146,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);