diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 62e84ff5f5..22bbe784bb 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -659,16 +659,6 @@ "count": 2 } }, - "src/view/com/util/List.tsx": { - "@typescript-eslint/no-explicit-any": { - "count": 2 - } - }, - "src/view/com/util/List.web.tsx": { - "@typescript-eslint/no-explicit-any": { - "count": 14 - } - }, "src/view/com/util/MainScrollProvider.tsx": { "@typescript-eslint/no-explicit-any": { "count": 2 diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx index aee397bb79..cde8d50c48 100644 --- a/src/view/com/util/List.tsx +++ b/src/view/com/util/List.tsx @@ -18,6 +18,8 @@ import {IS_IOS} from '#/env' import {FlatList_INTERNAL} from './Views' export type ListMethods = FlatList_INTERNAL +// This is a generic type; we could update ~30 call sites but this approach is consistent with RN internals. -dsb +// eslint-disable-next-line @typescript-eslint/no-explicit-any export type ListProps = Omit< FlatListPropsWithLayout, | 'onMomentumScrollBegin' // Use ScrollContext instead. @@ -58,7 +60,7 @@ let List = forwardRef( ...props }, ref, - ): React.ReactElement => { + ): React.ReactElement => { const isScrolledDown = useSharedValue(false) const t = useTheme() const dedupe = useDedupe(400) diff --git a/src/view/com/util/List.web.tsx b/src/view/com/util/List.web.tsx index 080758ed6a..86ad61aa4a 100644 --- a/src/view/com/util/List.web.tsx +++ b/src/view/com/util/List.web.tsx @@ -11,6 +11,7 @@ import { } from 'react' import { type FlatListProps, + type ListRenderItemInfo, StyleSheet, View, type ViewProps, @@ -23,7 +24,11 @@ import {useScrollHandlers} from '#/lib/ScrollContext' import {addStyle} from '#/lib/styles' import * as Layout from '#/components/Layout' -export type ListMethods = any // TODO: Better types. +export type ListMethods = { + scrollToTop: () => void + scrollToOffset: (options: {animated: boolean; offset: number}) => void + scrollToEnd: (options?: {animated?: boolean}) => void +} export type ListProps = Omit< FlatListProps, | 'onScroll' // Use ScrollContext instead. @@ -147,10 +152,16 @@ function ListImpl( scrollBy(options: ScrollToOptions) { element.scrollBy(options) }, - addEventListener(event: string, handler: any) { + addEventListener( + event: string, + handler: EventListenerOrEventListenerObject, + ) { element.addEventListener(event, handler) }, - removeEventListener(event: string, handler: any) { + removeEventListener( + event: string, + handler: EventListenerOrEventListenerObject, + ) { element.removeEventListener(event, handler) }, } @@ -180,10 +191,16 @@ function ListImpl( scrollBy(options: ScrollToOptions) { window.scrollBy(options) }, - addEventListener(event: string, handler: any) { + addEventListener( + event: string, + handler: EventListenerOrEventListenerObject, + ) { window.addEventListener(event, handler) }, - removeEventListener(event: string, handler: any) { + removeEventListener( + event: string, + handler: EventListenerOrEventListenerObject, + ) { window.removeEventListener(event, handler) }, } @@ -193,35 +210,28 @@ function ListImpl( const nativeRef = useRef(null) useImperativeHandle( ref, - () => - ({ - scrollToTop() { - getScrollableNode()?.scrollTo({top: 0}) - }, + () => ({ + scrollToTop() { + getScrollableNode()?.scrollTo({top: 0}) + }, - scrollToOffset({ - animated, - offset, - }: { - animated: boolean - offset: number - }) { - getScrollableNode()?.scrollTo({ - left: 0, - top: offset, - behavior: animated ? 'smooth' : 'instant', - }) - }, + scrollToOffset({animated, offset}: {animated: boolean; offset: number}) { + getScrollableNode()?.scrollTo({ + left: 0, + top: offset, + behavior: animated ? 'smooth' : 'instant', + }) + }, - scrollToEnd({animated = true}: {animated?: boolean}) { - const element = getScrollableNode() - element?.scrollTo({ - left: 0, - top: element.scrollHeight, - behavior: animated ? 'smooth' : 'instant', - }) - }, - }) as any, // TODO: Better types. + scrollToEnd({animated = true} = {}) { + const element = getScrollableNode() + element?.scrollTo({ + left: 0, + top: element.scrollHeight, + behavior: animated ? 'smooth' : 'instant', + }) + }, + }), [getScrollableNode], ) @@ -257,7 +267,7 @@ function ListImpl( | 'targetContentOffset' | 'contentInset' >, - null as any, + {}, ) }) @@ -326,7 +336,7 @@ function ListImpl( 'overflow-y': 'scroll', }, ]} - ref={nativeRef as any}> + ref={nativeRef as unknown as React.RefObject}> ({ renderItem: | null | undefined - | ((data: {index: number; item: any; separators: any}) => React.ReactNode) - extraData: any - onItemSeen: ((item: any) => void) | undefined + | ((info: ListRenderItemInfo) => React.ReactNode) + extraData: unknown + onItemSeen: ((item: ItemT) => void) | undefined }): React.ReactNode { const rowRef = useRef(null) const intersectionTimeout = useRef | undefined>( @@ -513,11 +523,24 @@ let Row = function RowImpl({ return ( - {renderItem({item, index, separators: null as any})} + {renderItem({ + item, + index, + separators: null as unknown as ListRenderItemInfo['separators'], + })} ) } -Row = memo(Row) +Row = memo(Row) as (props: { + item: ItemT + index: number + renderItem: + | null + | undefined + | ((info: ListRenderItemInfo) => React.ReactNode) + extraData: unknown + onItemSeen: ((item: ItemT) => void) | undefined +}) => React.ReactNode let Visibility = ({ root, @@ -572,7 +595,7 @@ Visibility = memo(Visibility) export const List = memo(forwardRef(ListImpl)) as ( props: ListProps & {ref?: React.Ref}, -) => React.ReactElement +) => React.ReactElement // https://stackoverflow.com/questions/7944460/detect-safari-browser