Skip to content

Commit 35591ca

Browse files
authored
Replace React Native type imports in EventTypes/eventUtils with structural equivalents (#4377)
## Description `v3/types/EventTypes.ts` and `v3/hooks/utils/eventUtils.ts` imported two types from react-native: `NativeSyntheticEvent` (events that may arrive wrapped) and `Animated.Value` (the leaf type of `Animated.event` argument mappings). This PR replaces both so the two files have zero react-native imports, with no behavior change — type annotations only. ## Test plan - `yarn ts-check` clean; `yarn test` 105/105; `yarn lint:js` 0 errors - New `eventTypes.test.ts` as above; no device run needed — types-only diff with zero runtime changes
1 parent a0c3095 commit 35591ca

4 files changed

Lines changed: 72 additions & 12 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { NativeSyntheticEvent } from 'react-native';
2+
import { Animated } from 'react-native';
3+
4+
import {
5+
isNativeAnimatedEvent,
6+
maybeExtractNativeEvent,
7+
} from '../v3/hooks/utils/eventUtils';
8+
import type { AnimatedEvent, NativeEventWrapper } from '../v3/types';
9+
10+
// Compile-time assertions (verified by `yarn ts-check`, which covers test
11+
// files): the structural stand-ins in EventTypes must stay supersets of the
12+
// real react-native types they replaced.
13+
type Payload = { handlerTag: number };
14+
15+
// A real NativeSyntheticEvent is assignable to the structural wrapper.
16+
const syntheticEvent = {} as NativeSyntheticEvent<Payload>;
17+
const _wrapped: NativeEventWrapper<Payload> = syntheticEvent;
18+
void _wrapped;
19+
20+
// A real Animated.Value is assignable as an argument-mapping leaf.
21+
const _animatedEvent: AnimatedEvent = {
22+
_argMapping: [{ nativeEvent: { handlerData: { x: new Animated.Value(0) } } }],
23+
};
24+
void _animatedEvent;
25+
26+
describe('event type structural stand-ins', () => {
27+
test('isNativeAnimatedEvent detects a real Animated.event object', () => {
28+
// The native-driver form is the one v3's `useAnimated` path consumes —
29+
// it returns an AnimatedEvent instance carrying `_argMapping`.
30+
const animatedEvent = Animated.event(
31+
[{ nativeEvent: { handlerData: { x: new Animated.Value(0) } } }],
32+
{ useNativeDriver: true }
33+
);
34+
35+
expect(isNativeAnimatedEvent(animatedEvent as never)).toBe(true);
36+
expect(isNativeAnimatedEvent(jest.fn())).toBe(false);
37+
expect(isNativeAnimatedEvent(undefined)).toBe(false);
38+
});
39+
40+
test('maybeExtractNativeEvent unwraps wrapped events and passes plain ones through', () => {
41+
const plain = {
42+
handlerTag: 1,
43+
state: 4,
44+
handlerData: { numberOfPointers: 1, pointerType: 0 },
45+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
46+
} as any;
47+
48+
expect(maybeExtractNativeEvent(plain)).toBe(plain);
49+
expect(maybeExtractNativeEvent({ nativeEvent: plain } as never)).toBe(
50+
plain
51+
);
52+
});
53+
});

‎packages/react-native-gesture-handler/src/v3/hooks/utils/eventUtils.ts‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import type { NativeSyntheticEvent } from 'react-native';
2-
31
import type { GestureTouchEvent } from '../../../handlers/gestureHandlerCommon';
42
import { tagMessage } from '../../../utils';
53
import type {
@@ -11,16 +9,15 @@ import type {
119
GestureHandlerEventWithHandlerData,
1210
GestureStateChangeEventWithHandlerData,
1311
GestureUpdateEventWithHandlerData,
12+
NativeEventWrapper,
1413
} from '../../types';
1514

1615
function isNativeEvent<THandlerData, TExtendedHandlerData extends THandlerData>(
1716
event: GestureHandlerEventWithHandlerData<THandlerData, TExtendedHandlerData>
1817
): event is
19-
| NativeSyntheticEvent<
20-
GestureUpdateEventWithHandlerData<TExtendedHandlerData>
21-
>
22-
| NativeSyntheticEvent<GestureStateChangeEventWithHandlerData<THandlerData>>
23-
| NativeSyntheticEvent<GestureTouchEvent> {
18+
| NativeEventWrapper<GestureUpdateEventWithHandlerData<TExtendedHandlerData>>
19+
| NativeEventWrapper<GestureStateChangeEventWithHandlerData<THandlerData>>
20+
| NativeEventWrapper<GestureTouchEvent> {
2421
'worklet';
2522

2623
return 'nativeEvent' in event;

‎packages/react-native-gesture-handler/src/v3/types/EventTypes.ts‎

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import type { Animated, NativeSyntheticEvent } from 'react-native';
1+
import type { BaseSyntheticEvent } from 'react';
22

33
import type { GestureTouchEvent } from '../../handlers/gestureHandlerCommon';
44
import type { PointerType } from '../../PointerType';
55
import type { State } from '../../State';
66

7+
export type NativeEventWrapper<T> = BaseSyntheticEvent<T, unknown, unknown>;
8+
79
type EventPayload = {
810
handlerTag: number;
911
state: State;
@@ -46,15 +48,15 @@ export type UnpackedGestureHandlerEventWithHandlerData<
4648

4749
export type UpdateEventWithHandlerData<THandlerData> =
4850
| GestureUpdateEventWithHandlerData<THandlerData>
49-
| NativeSyntheticEvent<GestureUpdateEventWithHandlerData<THandlerData>>;
51+
| NativeEventWrapper<GestureUpdateEventWithHandlerData<THandlerData>>;
5052

5153
export type StateChangeEventWithHandlerData<THandlerData> =
5254
| GestureStateChangeEventWithHandlerData<THandlerData>
53-
| NativeSyntheticEvent<GestureStateChangeEventWithHandlerData<THandlerData>>;
55+
| NativeEventWrapper<GestureStateChangeEventWithHandlerData<THandlerData>>;
5456

5557
export type TouchEvent =
5658
| GestureTouchEvent
57-
| NativeSyntheticEvent<GestureTouchEvent>;
59+
| NativeEventWrapper<GestureTouchEvent>;
5860

5961
export type GestureEvent<THandlerData> = {
6062
handlerTag: number;
@@ -68,10 +70,17 @@ export type UnpackedGestureHandlerEvent<THandlerData> =
6870
| GestureEvent<THandlerData>
6971
| GestureTouchEvent;
7072

73+
// Structural stand-in for RN's `Animated.Value` as a mapping leaf: the
74+
// mapping checks only walk the object structure and never call into the
75+
// value, so any non-mapping shape with Animated.Value's core method works.
76+
type AnimatedValue = {
77+
setValue: (value: number) => void;
78+
};
79+
7180
// This is not how Animated.event is typed in React Native. We add _argMapping in order to
7281
// have access to the _argMapping property to check for usage of `change*` callbacks.
7382
// It's also not typed as a function, which is breaking Gesture Handler type definitions.
74-
type AnimatedMapping = { [key: string]: AnimatedMapping } | Animated.Value;
83+
type AnimatedMapping = { [key: string]: AnimatedMapping } | AnimatedValue;
7584

7685
export type AnimatedEvent = {
7786
_argMapping: (AnimatedMapping | null)[];

‎packages/react-native-gesture-handler/src/v3/types/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export type {
1919
GestureStateChangeEventWithHandlerData,
2020
GestureUpdateEventWithHandlerData,
2121
HandlerData,
22+
NativeEventWrapper,
2223
StateChangeEventWithHandlerData,
2324
TouchEvent,
2425
UnpackedGestureHandlerEvent,

0 commit comments

Comments
 (0)