React 实现 forwardRef 的 TypeScript 泛型匿名函数组件
react-native
实现的
FlatList
组件使用了泛型组件的模式:
export class FlatList<ItemT = any> extends React.Component<FlatListProps<ItemT>>
这里我想要包装
FlatList
实现一些自己的功能,需要继承修改来自
react-native
的类型声明,还要保证代码风格与项目内其它的组件保持一致,那么它的格式应该类似:
interface TranscriptProps { /* ... */ }
const Transcript: React.FC<TranscriptProps> = (props) => { /* ... */ }
这里的
FlatList
需要一个泛型
ItemT
来提供对内部渲染的列表项目的类型提示,但使用了
React.FC
声明类型的函数组件却不能再添加泛型声明了,最后是在 stackoverflow 上找到了
一篇解答
,里面提供了一种不错的方法。
大概的解决方法就是将
React.FC
进行等效替换:
React.FC<P = {}>
基本等效于:
<P = {}>(props: PropsWithChildren<P>, context?: any) => ReactElement | null
在
P
的外层直接包裹
FlatListProps
,于是传入泛型的目标从
Props
接口变成了
FlatListProps
内部列表项目的接口,即
{ data: P }
。
所以开头的代码改写之后就变成了:
interface TranscriptProps<ItemT> extends FlatListProps<ItemT> { /* ... */ }
const Transcript: <ItemT = any>(
props: TranscriptProps<ItemT>
) => ReactElement | null = (props) => { /* ... */ }
❌ 如果还需要使用
forwardRef
的话,这里有
另一篇问答
提到了相应的解决方法。回答中提到了三种解决方法,这里我选择自定义一个 ref 的 prop:
interface TranscriptProps<ItemT> extends FlatListProps<ItemT> { /* ... */ }
const Transcript: <ItemT = any>(
props: TranscriptProps<ItemT> & { ref: Ref<FlatList> }
) => ReactElement | null = ({ ref, ...props }) => { /* ... */ }
✔️ 上面这个方案是当时直接照猫画虎写好的,后来运行才发现报错,下面提供一种各种方面都更优秀的方法:
interface TranscriptProps<ItemT> extends FlatListProps<ItemT> { /* ... */ }
export type TranscriptComponent<ItemT = any> = (
props: TranscriptProps<ItemT>,
ref?: Ref<FlatList<ItemT>>
) => ReactElement | null