I am trying to create a custom ManagedList
component which will have props
which has type ofFlatListProps<itemT>
or SectionListProps<itemT, SectionT = DefaultSectionT>
with additional { listType: ListType; dismissKeyboardOnPress?: boolean; dismissElevatedUIElements?: boolean;
properties.
To do so, I created a custom types ListType
and ListProps
:
type ListType = 'flat' | 'section';
type ListProps<listT extends ListType, itemT> = (
listT extends 'flat'
? FlatListProps<itemT>
: SectionListProps<itemT>
) & {
listType: ListType;
dismissKeyboardOnPress?: boolean;
dismissElevatedUIElements?: boolean;
};
Then I created a functional component which accepts generic paramter listT extends ListType
as shown below:
function ManagedList<listT extends ListType>(
props: Readonly<ListProps<listT, any>>
) {
const {
listType,
dismissKeyboardOnPress,
dismissElevatedUIElements,
data,
sections, // destructing sections throws error #1
renderItem,
...otherProps
} = props;
...
let listCmp;
if (listType === 'flat') {
listCmp = (
<FlatList
data={data}
renderItem={renderItem} // defining renderItem prop throws error #2
onTouchStart={touchStartHandler}
onLayout={layoutHandler}
onContentSizeChange={contentSizeChangeHandler}
onScroll={scrollHandler}
showsVerticalScrollIndicator={false}
{...otherProps}
/>
);
} else {
listCmp = (
<SectionList
sections={sections}
renderItem={renderItem} // defining renderItem prop throws error #2
onTouchStart={touchStartHandler}
onLayout={layoutHandler}
onContentSizeChange={contentSizeChangeHandler}
onScroll={scrollHandler}
showsVerticalScrollIndicator={false}
{...otherProps}
/>
);
}
return (
<View style={styles.listContainer}>
{listCmp}
{contentHeight > containerHeight && (
<Fragment>
<View style={styles.scrollbarTrack} />
<Animated.View
style={[styles.scrollbarThumb, animatedSyles]}
/>
</Fragment>
)}
</View>
);
As mentioned in the code, destructing sections
from props
throws the following error:
Property 'sections' does not exist on type 'Readonly<ListProps<listT, any>>'.ts(2339)
2nd error is thrown when defining renderItem
props. I believe this is caused because FlatList
and SectionList
has different renderItem
defnitions/types. FlatList
has the below type for renderItem
prop:
renderItem({
item: ItemT,
index: number,
separators: {
highlight: () => void;
unhighlight: () => void;
updateProps: (select: 'leading' | 'trailing', newProps: any) => void;
}
}): JSX.Element;
and SectionList
's renderItem
prop type:
renderItem({
item: ItemT,
index: number,
section: object, // <--- This is different
separators: {
highlight: () => void;
unhighlight: () => void;
updateProps: (select: 'leading' | 'trailing', newProps: any) => void;
}
}): JSX.Element;
To solve Error #1, I tried using Type Guards:
const {
listType,
dismissKeyboardOnPress,
dismissElevatedUIElements,
data,
renderItem,
...otherProps
} = props;
let sections;
if(props.sections) {
sections = props.section;
}
// or
const sections = props?.section ?? undefined;
But all these Type Guard attempts showed a similar error due to sections
not being present in props
object.
I'm not sure how I can solve Error #2.
NOTE: What I'm trying to accomplish is to have a single custom component to use FlatList
or SectionList
that have some event handlers assigned to them. Any other better approach to achieve this functionality can be a good answer.
type ListType = 'flat' | 'section';
type SharedListProps = {
listType: ListType;
dismissKeyboardOnPress:boolean;
dismissElevatedUIElements:boolean;
};
type fListProps<T> = FlatListProps<T> & SharedListProps & {listType: 'flat'};
type sListProps<T> = SectionListProps<T> &
SharedListProps & {listType: 'section'};
type ListProps<T> = fListProps<T> | sListProps<T>;
function MyList<T>(props: ListProps<T>) {
const {
listType,
dismissKeyboardOnPress,
dismissElevatedUIElements,
renderItem,
/*
until listType value is checked typescript is unable to infer
whether flatlist/sectionlist specific properties should exist
*/
// data,
// sections,
...otherProps
} = props;
if (props.listType == 'flat') {
// access flatlist specific props here
console.log(props.data);
return <FlatList {...props} />;
}
// access section list specific props here
console.log(props.sections);
return <SectionList {...props} />;
}