Skip to content

Commit 56e099a

Browse files
huntiefacebook-github-bot
authored andcommitted
Fix ref type compatibility for builtin components (#56673)
Summary: Fix `useRef` and `forwardRef` backcompat when migrating to React Native's generated TypeScript types (Strict TypeScript API). Previously this was an awkward breaking change: https://reactnative.dev/docs/strict-typescript-api#some-core-components-are-now-function-components-instead-of-class-components ```diff - const ref = useRef<View>(null); + const ref = useRef<React.ComponentRef<typeof View>>(null); ``` After this diff, no workaround will be required. **Problem detail** In our Flow source code, built-in components like `View` and `TextInput` are function components rather than classes. This differs from the previous manual `.d.ts` files which defined each component as a class. In TypeScript, a class name serves as both a value (the constructor) and a type (the instance), so patterns like `useRef<View>()` work. However, with function components, the name is only a value (the function signature) making `useRef<View>()` resolve to a ref holding the function itself, not the native element. **The fix**: Add companion type aliases alongside each component's value export. TypeScript's declaration merging makes the component name simultaneously a value (for JSX) and a type (for refs). ```diff // Before — required workaround - const ref = useRef<View>(null); + const ref = useRef<React.ComponentRef<typeof View>>(null); ref.current?.measure(...); - const inputRef = useRef<TextInput>(null); + const inputRef = useRef<React.ComponentRef<typeof TextInput>>(null); inputRef.current?.focus(); // After — original patterns work as-is const ref = useRef<View>(null); // View as a type = HostInstance ref.current?.measure(...); // HostInstance has measure() const inputRef = useRef<TextInput>(null); // TextInput as a type = TextInputInstance inputRef.current?.focus(); // TextInputInstance has focus() ``` Changelog: [General][Fixed] - Add companion instance type aliases for built-in components, preserving `useRef<Component>` patterns under the Strict TypeScript API Differential Revision: D103601923
1 parent aadbe96 commit 56e099a

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

packages/react-native/index.js.flow

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,26 @@
2222

2323
// #region Components
2424

25+
// These types are mapped to a companion type export for each component,
26+
// enabling ref type compatibility in TypeScript (e.g. `useRef<View>()`).
27+
import type {PublicScrollViewInstance as _PublicScrollViewInstance} from './Libraries/Components/ScrollView/ScrollView';
28+
import type {TextInputInstance as _TextInputInstance} from './Libraries/Components/TextInput/TextInput.flow';
29+
import type {HostInstance as _HostInstance} from './src/private/types/HostInstance';
30+
2531
export type {ActivityIndicatorProps} from './Libraries/Components/ActivityIndicator/ActivityIndicator';
2632
export {default as ActivityIndicator} from './Libraries/Components/ActivityIndicator/ActivityIndicator';
33+
export type ActivityIndicator = _HostInstance;
2734

2835
export type {ButtonProps} from './Libraries/Components/Button';
2936
export {default as Button} from './Libraries/Components/Button';
37+
export type Button = _HostInstance;
3038

3139
export type {
3240
DrawerLayoutAndroidProps,
3341
DrawerSlideEvent,
3442
} from './Libraries/Components/DrawerAndroid/DrawerLayoutAndroid';
3543
export {default as DrawerLayoutAndroid} from './Libraries/Components/DrawerAndroid/DrawerLayoutAndroid';
44+
export type DrawerLayoutAndroid = _HostInstance;
3645

3746
export type {FlatListProps} from './Libraries/Lists/FlatList';
3847
export {default as FlatList} from './Libraries/Lists/FlatList';
@@ -56,13 +65,17 @@ export type {
5665
ImageURISource,
5766
} from './Libraries/Image/ImageSource';
5867
export {default as Image} from './Libraries/Image/Image';
68+
export type Image = _HostInstance;
5969
export {default as ImageBackground} from './Libraries/Image/ImageBackground';
70+
export type ImageBackground = _HostInstance;
6071

6172
export type {InputAccessoryViewProps} from './Libraries/Components/TextInput/InputAccessoryView';
6273
export {default as InputAccessoryView} from './Libraries/Components/TextInput/InputAccessoryView';
74+
export type InputAccessoryView = _HostInstance;
6375

6476
export type {KeyboardAvoidingViewProps} from './Libraries/Components/Keyboard/KeyboardAvoidingView';
6577
export {default as KeyboardAvoidingView} from './Libraries/Components/Keyboard/KeyboardAvoidingView';
78+
export type KeyboardAvoidingView = _HostInstance;
6679

6780
export type {LayoutConformanceProps} from './Libraries/Components/LayoutConformance/LayoutConformance';
6881
export {default as experimental_LayoutConformance} from './Libraries/Components/LayoutConformance/LayoutConformance';
@@ -74,25 +87,30 @@ export type {
7487
ModalPropsIOS,
7588
} from './Libraries/Modal/Modal';
7689
export {default as Modal} from './Libraries/Modal/Modal';
90+
export type Modal = _HostInstance;
7791

7892
export type {
7993
PressableAndroidRippleConfig,
8094
PressableProps,
8195
PressableStateCallbackType,
8296
} from './Libraries/Components/Pressable/Pressable';
8397
export {default as Pressable} from './Libraries/Components/Pressable/Pressable';
98+
export type Pressable = _HostInstance;
8499

85100
export type {ProgressBarAndroidProps} from './Libraries/Components/ProgressBarAndroid/ProgressBarAndroid';
86101
export {default as ProgressBarAndroid} from './Libraries/Components/ProgressBarAndroid/ProgressBarAndroid';
102+
export type ProgressBarAndroid = _HostInstance;
87103

88104
export type {
89105
RefreshControlProps,
90106
RefreshControlPropsAndroid,
91107
RefreshControlPropsIOS,
92108
} from './Libraries/Components/RefreshControl/RefreshControl';
93109
export {default as RefreshControl} from './Libraries/Components/RefreshControl/RefreshControl';
110+
export type RefreshControl = _HostInstance;
94111

95112
export {default as SafeAreaView} from './Libraries/Components/SafeAreaView/SafeAreaView';
113+
export type SafeAreaView = _HostInstance;
96114

97115
export type {
98116
ScrollViewImperativeMethods,
@@ -103,6 +121,7 @@ export type {
103121
ScrollViewPropsIOS,
104122
} from './Libraries/Components/ScrollView/ScrollView';
105123
export {default as ScrollView} from './Libraries/Components/ScrollView/ScrollView';
124+
export type ScrollView = _PublicScrollViewInstance;
106125

107126
export type {
108127
SectionListProps,
@@ -118,15 +137,18 @@ export type {
118137
StatusBarStyle,
119138
} from './Libraries/Components/StatusBar/StatusBar';
120139
export {default as StatusBar} from './Libraries/Components/StatusBar/StatusBar';
140+
export type StatusBar = _HostInstance;
121141

122142
export type {
123143
SwitchChangeEvent,
124144
SwitchProps,
125145
} from './Libraries/Components/Switch/Switch';
126146
export {default as Switch} from './Libraries/Components/Switch/Switch';
147+
export type Switch = _HostInstance;
127148

128149
export type {TextProps} from './Libraries/Text/Text';
129150
export {default as Text} from './Libraries/Text/Text';
151+
export type Text = _HostInstance;
130152
export type {NativeTextProps as unstable_NativeTextProps} from './Libraries/Text/TextNativeComponent';
131153
export {NativeText as unstable_NativeText} from './Libraries/Text/TextNativeComponent';
132154
export {default as unstable_TextAncestorContext} from './Libraries/Text/TextAncestorContext';
@@ -151,20 +173,25 @@ export type {
151173
SubmitBehavior,
152174
} from './Libraries/Components/TextInput/TextInput';
153175
export {default as TextInput} from './Libraries/Components/TextInput/TextInput';
176+
export type TextInput = _TextInputInstance;
154177

155178
export {default as Touchable} from './Libraries/Components/Touchable/Touchable';
156179

157180
export type {TouchableHighlightProps} from './Libraries/Components/Touchable/TouchableHighlight';
158181
export {default as TouchableHighlight} from './Libraries/Components/Touchable/TouchableHighlight';
182+
export type TouchableHighlight = _HostInstance;
159183

160184
export type {TouchableNativeFeedbackProps} from './Libraries/Components/Touchable/TouchableNativeFeedback';
161185
export {default as TouchableNativeFeedback} from './Libraries/Components/Touchable/TouchableNativeFeedback';
186+
export type TouchableNativeFeedback = _HostInstance;
162187

163188
export type {TouchableOpacityProps} from './Libraries/Components/Touchable/TouchableOpacity';
164189
export {default as TouchableOpacity} from './Libraries/Components/Touchable/TouchableOpacity';
190+
export type TouchableOpacity = _HostInstance;
165191

166192
export type {TouchableWithoutFeedbackProps} from './Libraries/Components/Touchable/TouchableWithoutFeedback';
167193
export {default as TouchableWithoutFeedback} from './Libraries/Components/Touchable/TouchableWithoutFeedback';
194+
export type TouchableWithoutFeedback = _HostInstance;
168195

169196
export type {
170197
AccessibilityActionEvent,
@@ -182,6 +209,7 @@ export type {
182209
ViewPropsIOS,
183210
} from './Libraries/Components/View/ViewPropTypes';
184211
export {default as View} from './Libraries/Components/View/View';
212+
export type View = _HostInstance;
185213
export {default as unstable_NativeView} from './Libraries/Components/View/ViewNativeComponent';
186214

187215
export type {

0 commit comments

Comments
 (0)