From c9dbc4cf5a5550eefc41063b88487cc13f45f695 Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Fri, 3 Jul 2026 14:27:41 +0200 Subject: [PATCH] preserve SingleGestureName in gesture types --- .../src/handlers/handlersRegistry.ts | 12 ++++++----- .../src/v3/hooks/gestures/fling/FlingTypes.ts | 5 ++++- .../src/v3/hooks/gestures/hover/HoverTypes.ts | 4 +++- .../src/v3/hooks/gestures/index.ts | 13 ++---------- .../gestures/longPress/LongPressTypes.ts | 5 ++++- .../gestures/longPress/useLongPressGesture.ts | 10 ++++++---- .../v3/hooks/gestures/manual/ManualTypes.ts | 5 ++++- .../v3/hooks/gestures/native/NativeTypes.ts | 5 ++++- .../src/v3/hooks/gestures/pan/PanTypes.ts | 4 +++- .../v3/hooks/gestures/pan/usePanGesture.ts | 3 ++- .../src/v3/hooks/gestures/pinch/PinchTypes.ts | 4 +++- .../hooks/gestures/rotation/RotationTypes.ts | 4 +++- .../v3/hooks/gestures/singleGestureUnion.ts | 20 +++++++++++++++++++ .../src/v3/hooks/gestures/tap/TapTypes.ts | 5 ++++- .../v3/hooks/gestures/tap/useTapGesture.ts | 10 ++++++---- .../src/v3/hooks/useGesture.ts | 5 +++-- .../src/v3/types/GestureTypes.ts | 11 ++++++---- 17 files changed, 85 insertions(+), 40 deletions(-) create mode 100644 packages/react-native-gesture-handler/src/v3/hooks/gestures/singleGestureUnion.ts diff --git a/packages/react-native-gesture-handler/src/handlers/handlersRegistry.ts b/packages/react-native-gesture-handler/src/handlers/handlersRegistry.ts index 2c4426d974..b64549a3e5 100644 --- a/packages/react-native-gesture-handler/src/handlers/handlersRegistry.ts +++ b/packages/react-native-gesture-handler/src/handlers/handlersRegistry.ts @@ -1,4 +1,5 @@ import { isTestEnv } from '../utils'; +import type { AnySingleGesture } from '../v3/hooks/gestures/singleGestureUnion'; import type { SingleGesture } from '../v3/types'; import type { GestureEvent, @@ -8,10 +9,9 @@ import type { GestureType } from './gestures/gesture'; export const handlerIDToTag: Record = {}; -// There were attempts to create types that merge possible HandlerData and Config, -// but ts was not able to infer them properly in many cases, so we use any here. -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const hookGestures = new Map>(); +// Stored as the discriminated union of concrete v3 gestures so lookups by +// test ID can narrow on the `type` field. +const hookGestures = new Map(); const gestures = new Map(); const oldHandlers = new Map(); const testIDs = new Map(); @@ -25,7 +25,9 @@ export function registerGesture< gesture: SingleGesture ) { if (isTestEnv() && gesture.config.testID) { - hookGestures.set(handlerTag, gesture); + // The generic parameters cannot be correlated with the union members + // here, but every gesture created by the v3 hooks is a union member. + hookGestures.set(handlerTag, gesture as unknown as AnySingleGesture); testIDs.set(gesture.config.testID, handlerTag); } } diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/fling/FlingTypes.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/fling/FlingTypes.ts index cf093c3afa..fea6a59927 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/fling/FlingTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/fling/FlingTypes.ts @@ -3,6 +3,7 @@ import type { ExcludeInternalConfigProps, GestureEvent, SingleGesture, + SingleGestureName, WithSharedValue, } from '../../../types'; @@ -56,5 +57,7 @@ export type FlingGestureActiveEvent = FlingGestureEvent; export type FlingGesture = SingleGesture< FlingGestureProperties, - FlingHandlerData + FlingHandlerData, + FlingHandlerData, + SingleGestureName.Fling >; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/hover/HoverTypes.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/hover/HoverTypes.ts index 5bf13293cc..123f67c8b2 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/hover/HoverTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/hover/HoverTypes.ts @@ -5,6 +5,7 @@ import type { ExcludeInternalConfigProps, GestureEvent, SingleGesture, + SingleGestureName, WithSharedValue, } from '../../../types'; @@ -72,5 +73,6 @@ export type HoverGestureActiveEvent = GestureEvent; export type HoverGesture = SingleGesture< HoverGestureInternalProperties, HoverHandlerData, - HoverExtendedHandlerData + HoverExtendedHandlerData, + SingleGestureName.Hover >; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/index.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/index.ts index ca6c7fd015..a990ed2b91 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/index.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/index.ts @@ -124,17 +124,8 @@ export type { PanGestureEvent, }; export { usePanGesture } from './pan/usePanGesture'; - -export type SingleGesture = - | TapGesture - | FlingGesture - | LongPressGesture - | PinchGesture - | RotationGesture - | HoverGesture - | ManualGesture - | NativeGesture - | PanGesture; +export type { AnySingleGesture } from './singleGestureUnion'; +export type { AnySingleGesture as SingleGesture } from './singleGestureUnion'; /* eslint-disable @typescript-eslint/no-duplicate-type-constituents */ export type SingleGestureEvent = diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/longPress/LongPressTypes.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/longPress/LongPressTypes.ts index 5736206cf7..ca80660f20 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/longPress/LongPressTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/longPress/LongPressTypes.ts @@ -3,6 +3,7 @@ import type { ExcludeInternalConfigProps, GestureEvent, SingleGesture, + SingleGestureName, WithSharedValue, } from '../../../types'; @@ -68,5 +69,7 @@ export type LongPressGestureActiveEvent = LongPressGestureEvent; export type LongPressGesture = SingleGesture< LongPressGestureProperties, - LongPressHandlerData + LongPressHandlerData, + LongPressHandlerData, + SingleGestureName.LongPress >; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/longPress/useLongPressGesture.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/longPress/useLongPressGesture.ts index 144dd4d7d8..5ae23c7911 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/longPress/useLongPressGesture.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/longPress/useLongPressGesture.ts @@ -39,8 +39,10 @@ export function useLongPressGesture( LongPressGestureInternalProperties >(config, LongPressPropsMapping, transformLongPressProps); - return useGesture( - SingleGestureName.LongPress, - longPressConfig - ); + return useGesture< + LongPressGestureInternalProperties, + LongPressHandlerData, + LongPressHandlerData, + SingleGestureName.LongPress + >(SingleGestureName.LongPress, longPressConfig); } diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/manual/ManualTypes.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/manual/ManualTypes.ts index 81946bcac7..7feed55b71 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/manual/ManualTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/manual/ManualTypes.ts @@ -3,6 +3,7 @@ import type { ExcludeInternalConfigProps, GestureEvent, SingleGesture, + SingleGestureName, } from '../../../types'; export type ManualGestureNativeProperties = Record; @@ -24,5 +25,7 @@ export type ManualGestureActiveEvent = ManualGestureEvent; export type ManualGesture = SingleGesture< ManualGestureProperties, - ManualHandlerData + ManualHandlerData, + ManualHandlerData, + SingleGestureName.Manual >; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/native/NativeTypes.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/native/NativeTypes.ts index fb3026e7b9..726232f630 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/native/NativeTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/native/NativeTypes.ts @@ -3,6 +3,7 @@ import type { ExcludeInternalConfigProps, GestureEvent, SingleGesture, + SingleGestureName, WithSharedValue, } from '../../../types'; @@ -58,5 +59,7 @@ export type NativeGestureActiveEvent = NativeGestureEvent; export type NativeGesture = SingleGesture< NativeGestureProperties, - NativeHandlerData + NativeHandlerData, + NativeHandlerData, + SingleGestureName.Native >; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/pan/PanTypes.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/pan/PanTypes.ts index db5b879912..3d70bfa269 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/pan/PanTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/pan/PanTypes.ts @@ -4,6 +4,7 @@ import type { ExcludeInternalConfigProps, GestureEvent, SingleGesture, + SingleGestureName, WithSharedValue, } from '../../../types'; @@ -170,5 +171,6 @@ export type PanGestureActiveEvent = GestureEvent; export type PanGesture = SingleGesture< PanGestureInternalProperties, PanHandlerData, - PanExtendedHandlerData + PanExtendedHandlerData, + SingleGestureName.Pan >; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/pan/usePanGesture.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/pan/usePanGesture.ts index 0f714421c0..44b518dfc9 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/pan/usePanGesture.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/pan/usePanGesture.ts @@ -158,6 +158,7 @@ export function usePanGesture( return useGesture< PanGestureInternalProperties, PanHandlerData, - PanExtendedHandlerData + PanExtendedHandlerData, + SingleGestureName.Pan >(SingleGestureName.Pan, panConfig); } diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/pinch/PinchTypes.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/pinch/PinchTypes.ts index e6b7aa9877..d0f64fffb2 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/pinch/PinchTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/pinch/PinchTypes.ts @@ -3,6 +3,7 @@ import type { ExcludeInternalConfigProps, GestureEvent, SingleGesture, + SingleGestureName, } from '../../../types'; export type PinchGestureNativeProperties = Record; @@ -36,5 +37,6 @@ export type PinchGestureActiveEvent = GestureEvent; export type PinchGesture = SingleGesture< PinchGestureProperties, PinchHandlerData, - PinchExtendedHandlerData + PinchExtendedHandlerData, + SingleGestureName.Pinch >; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/rotation/RotationTypes.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/rotation/RotationTypes.ts index 9327ca1d67..abb0f0c42d 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/rotation/RotationTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/rotation/RotationTypes.ts @@ -3,6 +3,7 @@ import type { ExcludeInternalConfigProps, GestureEvent, SingleGesture, + SingleGestureName, } from '../../../types'; export type RotationGestureNativeProperties = Record; @@ -37,5 +38,6 @@ export type RotationGestureActiveEvent = export type RotationGesture = SingleGesture< RotationGestureProperties, RotationHandlerData, - RotationExtendedHandlerData + RotationExtendedHandlerData, + SingleGestureName.Rotation >; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/singleGestureUnion.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/singleGestureUnion.ts new file mode 100644 index 0000000000..f42b78a8a5 --- /dev/null +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/singleGestureUnion.ts @@ -0,0 +1,20 @@ +import type { FlingGesture } from './fling/FlingTypes'; +import type { HoverGesture } from './hover/HoverTypes'; +import type { LongPressGesture } from './longPress/LongPressTypes'; +import type { ManualGesture } from './manual/ManualTypes'; +import type { NativeGesture } from './native/NativeTypes'; +import type { PanGesture } from './pan/PanTypes'; +import type { PinchGesture } from './pinch/PinchTypes'; +import type { RotationGesture } from './rotation/RotationTypes'; +import type { TapGesture } from './tap/TapTypes'; + +export type AnySingleGesture = + | TapGesture + | FlingGesture + | LongPressGesture + | PinchGesture + | RotationGesture + | HoverGesture + | ManualGesture + | NativeGesture + | PanGesture; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/tap/TapTypes.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/tap/TapTypes.ts index 4c1dfb1fbb..62e9b73ab6 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/tap/TapTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/tap/TapTypes.ts @@ -3,6 +3,7 @@ import type { DiscreteSingleGesture, ExcludeInternalConfigProps, GestureEvent, + SingleGestureName, WithSharedValue, } from '../../../types'; @@ -96,5 +97,7 @@ export type TapGestureActiveEvent = TapGestureEvent; export type TapGesture = DiscreteSingleGesture< TapGestureInternalProperties, - TapHandlerData + TapHandlerData, + TapHandlerData, + SingleGestureName.Tap >; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/gestures/tap/useTapGesture.ts b/packages/react-native-gesture-handler/src/v3/hooks/gestures/tap/useTapGesture.ts index edd8c654e4..de2b8196f0 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/gestures/tap/useTapGesture.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/gestures/tap/useTapGesture.ts @@ -29,8 +29,10 @@ export function useTapGesture( TapGestureInternalProperties >(config, TapPropsMapping); - return useGesture( - SingleGestureName.Tap, - tapConfig - ); + return useGesture< + TapGestureInternalProperties, + TapHandlerData, + TapHandlerData, + SingleGestureName.Tap + >(SingleGestureName.Tap, tapConfig); } diff --git a/packages/react-native-gesture-handler/src/v3/hooks/useGesture.ts b/packages/react-native-gesture-handler/src/v3/hooks/useGesture.ts index ff55841506..3871cdebca 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/useGesture.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/useGesture.ts @@ -25,10 +25,11 @@ export function useGesture< TConfig, THandlerData, TExtendedHandlerData extends THandlerData = THandlerData, + TType extends SingleGestureName = SingleGestureName, >( - type: SingleGestureName, + type: TType, config: BaseGestureConfig -): SingleGesture { +): SingleGesture { const handlerTag = useMemo(() => getNextHandlerTag(), []); const disableReanimated = useMemo(() => config.disableReanimated, []); diff --git a/packages/react-native-gesture-handler/src/v3/types/GestureTypes.ts b/packages/react-native-gesture-handler/src/v3/types/GestureTypes.ts index d78e7adc34..fb7284dba4 100644 --- a/packages/react-native-gesture-handler/src/v3/types/GestureTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/types/GestureTypes.ts @@ -39,9 +39,10 @@ export type SingleGesture< TConfig, THandlerData, TExtendedHandlerData extends THandlerData = THandlerData, + TType extends SingleGestureName = SingleGestureName, > = { handlerTag: number; - type: SingleGestureName; + type: TType; config: BaseGestureConfig; detectorCallbacks: DetectorCallbacks; gestureRelations: GestureRelations; @@ -51,17 +52,19 @@ export type DiscreteSingleGesture< TConfig, THandlerData, TExtendedHandlerData extends THandlerData = THandlerData, + TType extends SingleGestureName = SingleGestureName, > = { [K in keyof SingleGesture< TConfig, THandlerData, - TExtendedHandlerData + TExtendedHandlerData, + TType >]: K extends 'config' ? Omit< - SingleGesture[K], + SingleGesture[K], 'onUpdate' > - : SingleGesture[K]; + : SingleGesture[K]; }; export type ComposedGesture = {