Enable React Native strict TypeScript API (#11459)

This commit is contained in:
Samuel Newman
2026-08-25 09:40:14 +03:00
committed by GitHub
parent 5f3d24efab
commit f87fdd2ea2
149 changed files with 506 additions and 623 deletions
-1
View File
@@ -271,7 +271,6 @@ function HashtagScreenTab({
onEndReached={onEndReached}
onEndReachedThreshold={4}
onItemSeen={trackPostView}
// @ts-ignore web only -prf
desktopFixedHeight
ListFooterComponent={
<ListFooter
+2 -2
View File
@@ -78,8 +78,8 @@ export const LoginForm = ({
const [identifier, setIdentifier] = useState(initialHandle || '')
const [identifierFocused, setIdentifierFocused] = useState(false)
const [authFactorToken, setAuthFactorToken] = useState('')
const identifierRef = useRef<TextInput>(null)
const passwordRef = useRef<TextInput>(null)
const identifierRef = useRef<React.ComponentRef<typeof TextInput>>(null)
const passwordRef = useRef<React.ComponentRef<typeof TextInput>>(null)
const hasFocusedOnce = useRef(false)
const [hasPassword, setHasPassword] = useState(false)
const [revealPassword, setRevealPassword] = useState(false)
+1 -1
View File
@@ -379,7 +379,7 @@ function JoinRequestsList({
style={[a.flex_1, a.align_center, a.justify_center, a.py_4xl]}>
<Loader size="xl" />
</View>
) : null
) : undefined
}
contentContainerStyle={
showFooter ? {paddingBottom: footerHeight} : undefined
@@ -189,7 +189,7 @@ export function MessageComposer({
<View
collapsable={false}
ref={native(
(node: View) =>
(node: React.ComponentRef<typeof View>) =>
void composerInternalApiRef.current?.setAutocompleteAnchor(node),
)}>
<GlassContainer
@@ -209,7 +209,9 @@ export function MessageComposer({
composerInternalApiRef.current?.insert(emoji.native)
}
nextFocusRef={() =>
composerInternalApiRef.current?.input?.element
composerInternalApiRef.current?.input
?.element as unknown as
{focus: () => void} | null | undefined
}>
<EmojiPicker.Trigger label={l`Open emoji picker`}>
{({props, state, control}) => (
+1 -1
View File
@@ -35,7 +35,7 @@ export function Layout({children}: React.PropsWithChildren<{}>) {
const {gtMobile} = useBreakpoints()
const onboardDispatch = useOnboardingDispatch()
const {state, dispatch} = useOnboardingInternalState()
const scrollview = useRef<ScrollView>(null)
const scrollview = useRef<React.ComponentRef<typeof ScrollView>>(null)
const prevActiveStep = useRef<string>(state.activeStep)
useEffect(() => {
@@ -1,6 +1,7 @@
import {useRef, useState} from 'react'
import {View} from 'react-native'
import PagerView from 'react-native-pager-view'
import {type PagerViewOnPageSelectedEvent} from 'react-native-pager-view'
import {Image} from 'expo-image'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -36,7 +37,7 @@ export function ValuePropositionPager({
ref={ref}
style={[a.flex_1]}
initialPage={step}
onPageSelected={evt => {
onPageSelected={(evt: PagerViewOnPageSelectedEvent) => {
const page = evt.nativeEvent.position as 0 | 1 | 2
if (step !== page) {
setActivePage(page)
@@ -52,7 +52,6 @@ export const PlaceholderCanvas = forwardRef<PlaceholderCanvasRef, {}>(
<View style={styles.container}>
<Suspense fallback={null}>
<LazyViewShot
// @ts-ignore this library doesn't have types
ref={viewshotRef}
options={{
fileName: 'placeholderAvatar',
@@ -378,7 +378,7 @@ function SuggestedProfileCard({
}) {
const t = useTheme()
const ax = useAnalytics()
const cardRef = useRef<View>(null)
const cardRef = useRef<React.ComponentRef<typeof View>>(null)
const hasTrackedRef = useRef(false)
useEffect(() => {
@@ -396,7 +396,7 @@ function SuggestedProfileCard({
},
{threshold: 0.5},
)
// @ts-ignore - web only
// @ts-expect-error - web only
observer.observe(node)
return () => observer.disconnect()
} else {
+2 -2
View File
@@ -160,8 +160,8 @@ export function PostThread({uri}: {uri: string}) {
const totalParentCount = useRef(0) // recomputed below
const totalChildrenCount = useRef(thread.data.items.length) // recomputed below
const listRef = useRef<ListMethods>(null)
const anchorRef = useRef<View | null>(null)
const headerRef = useRef<View | null>(null)
const anchorRef = useRef<React.ComponentRef<typeof View> | null>(null)
const headerRef = useRef<React.ComponentRef<typeof View> | null>(null)
/*
* On a cold load, parents are not prepended until the anchor post has
-1
View File
@@ -128,7 +128,6 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
onRetry={fetchNextPage}
/>
}
// @ts-ignore our .web version only -prf
desktopFixedHeight
initialNumToRender={initialNumToRender}
windowSize={11}
+2 -1
View File
@@ -1,6 +1,7 @@
import {useCallback, useMemo, useRef, useState} from 'react'
import {View, type ViewabilityConfig} from 'react-native'
import {View} from 'react-native'
import {Trans, useLingui} from '@lingui/react/macro'
import {type ViewabilityConfig} from '@react-native/virtualized-lists'
import {useQueryClient} from '@tanstack/react-query'
import * as bcp47Match from 'bcp-47-match'
+1 -1
View File
@@ -115,7 +115,7 @@ export function SearchScreenShell({
const {gtMobile} = useBreakpoints()
const navigation = useNavigation<NavigationProp>()
const route = useRoute()
const textInput = useRef<TextInput>(null)
const textInput = useRef<React.ComponentRef<typeof TextInput>>(null)
const {t: l} = useLingui()
const {currentAccount} = useSession()
const queryClient = useQueryClient()
@@ -28,7 +28,7 @@ export function ClearableInput({
}) {
const t = useTheme()
const {t: l} = useLingui()
const inputRef = useRef<TextInput>(null)
const inputRef = useRef<React.ComponentRef<typeof TextInput>>(null)
const [showClear, setShowClear] = useState(defaultValue.length > 0)
return (
@@ -137,8 +137,8 @@ function DialogInner({
const [dateUntilActive, setDateUntilActive] = useState(!!parsed.until)
const [filters, setFilters] = useState<AdvancedFilter[]>(parsed.filters)
const scrollRef = useRef<ScrollView>(null)
const filtersSectionRef = useRef<View>(null)
const scrollRef = useRef<React.ComponentRef<typeof ScrollView>>(null)
const filtersSectionRef = useRef<React.ComponentRef<typeof View>>(null)
function addFilter() {
if (filters.length >= MAX_FILTERS) return
@@ -37,7 +37,7 @@ export function SearchAutocompleteInput({
* keystroke while the input is still focused.
*/
const [dismissed, setDismissed] = useState(false)
const inputRef = useRef<TextInput>(null)
const inputRef = useRef<React.ComponentRef<typeof TextInput>>(null)
const sift = useSift({
offset: a.p_sm.padding,
@@ -1,9 +1,9 @@
import {type ImageSourcePropType} from 'react-native'
import {type ImageSource} from 'expo-image'
import type * as DynamicAppIcon from '@bsky.app/expo-dynamic-app-icon'
export type AppIconSet = {
id: DynamicAppIcon.IconName
name: string
iosImage: () => ImageSourcePropType
androidImage: () => ImageSourcePropType
iosImage: () => ImageSource
androidImage: () => ImageSource
}
@@ -128,7 +128,7 @@ export function ActivityNotificationSettingsScreen({}: Props) {
onEndReached={onEndReached}
onEndReachedThreshold={4}
ListEmptyComponent={
error ? null : (
error ? undefined : (
<View style={[a.px_xl, a.py_md]}>
{!isPending ? (
<Admonition.Outer type="tip">
@@ -73,7 +73,7 @@ function DeleteAccountDialogInner({
control: DialogOuterProps['control']
deactivateDialogControl: DialogOuterProps['control']
}) {
const passwordRef = useRef<TextInput | null>(null)
const passwordRef = useRef<React.ComponentRef<typeof TextInput> | null>(null)
const t = useTheme()
const {_} = useLingui()
const cleanError = useCleanError()
@@ -3,7 +3,6 @@ import {StyleSheet} from 'react-native'
import {type CaptchaWebViewProps} from './CaptchaWebView.shared'
// @ts-ignore web only, we will always redirect to the app on web (CORS)
const REDIRECT_HOST = new URL(window.location.href).host
export function CaptchaWebView({
@@ -25,13 +24,11 @@ export function CaptchaWebView({
}, [onError])
const onLoad = useCallback(() => {
// @ts-ignore web
const frame: HTMLIFrameElement = document.getElementById(
'captcha-iframe',
) as HTMLIFrameElement
try {
// @ts-ignore web
const href = frame?.contentWindow?.location.href
if (!href) return
const urlp = new URL(href)
+2 -2
View File
@@ -68,8 +68,8 @@ export function StepInfo({
const prevEmailValueRef = useRef<string>(state.email)
const passwordValueRef = useRef<string>(state.password)
const emailInputRef = useRef<TextInput>(null)
const passwordInputRef = useRef<TextInput>(null)
const emailInputRef = useRef<React.ComponentRef<typeof TextInput>>(null)
const passwordInputRef = useRef<React.ComponentRef<typeof TextInput>>(null)
const birthdateInputRef = useRef<DateFieldRef>(null)
const aaRegionConfig = useAgeAssuranceRegionConfigWithFallback()
@@ -41,6 +41,7 @@ import {
} from '#/state/shell/progress-guide'
import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader'
import {ProfileSubpageHeader} from '#/view/com/profile/ProfileSubpageHeader'
import {type ListRef} from '#/view/com/util/List'
import {bulkWriteFollows} from '#/screens/Onboarding/util'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
@@ -251,8 +252,7 @@ function StarterPackScreenLoaded({
// Validated above
listUri={starterPack.list!.uri}
headerHeight={headerHeight}
// @ts-expect-error
scrollElRef={scrollElRef}
scrollElRef={scrollElRef as ListRef}
moderationOpts={moderationOpts}
/>
)
@@ -260,11 +260,9 @@ function StarterPackScreenLoaded({
{showFeedsTab
? ({headerHeight, scrollElRef}) => (
<FeedsList
// @ts-expect-error ?
feeds={starterPack?.feeds}
feeds={starterPack.feeds!}
headerHeight={headerHeight}
// @ts-expect-error
scrollElRef={scrollElRef}
scrollElRef={scrollElRef as ListRef}
/>
)
: null}
@@ -274,9 +272,7 @@ function StarterPackScreenLoaded({
// Validated above
listUri={starterPack.list!.uri}
headerHeight={headerHeight}
// @ts-expect-error
scrollElRef={scrollElRef}
moderationOpts={moderationOpts}
scrollElRef={scrollElRef as ListRef}
/>
)
: null}
-1
View File
@@ -177,7 +177,6 @@ function TopicScreenTab({
onEndReached={onEndReached}
onEndReachedThreshold={4}
onItemSeen={trackPostView}
// @ts-ignore web only -prf
desktopFixedHeight
ListFooterComponent={
<ListFooter
+5 -3
View File
@@ -5,8 +5,6 @@ import {
Pressable,
ScrollView,
View,
type ViewabilityConfig,
type ViewToken,
} from 'react-native'
import {
Gesture,
@@ -29,6 +27,10 @@ import {AtUri} from '@atproto/syntax'
import {type ModerationDecision} from '@bsky/sdk/moderation'
import {RichText as RichTextAPI} from '@bsky/sdk/richtext'
import {Trans, useLingui} from '@lingui/react/macro'
import {
type ListViewToken as ViewToken,
type ViewabilityConfig,
} from '@react-native/virtualized-lists'
import {
type RouteProp,
useFocusEffect,
@@ -413,7 +415,7 @@ function Feed() {
const onViewableItemsChanged = useCallback(
({viewableItems}: {viewableItems: ViewToken[]; changed: ViewToken[]}) => {
if (viewableItems[0] && viewableItems[0].index !== null) {
if (viewableItems[0]?.index != null) {
const newIndex = viewableItems[0].index
setCurrentIndex(newIndex)
updateVideoState(newIndex)