Reputation: 14814
I'm building a React Native app with TypeScript. renderItem
complains that the destructured item implicitly has an any
type. I googled and found this question and tried to implement what they teach here combined with the types in index.d.ts
of the @types
package for React Native.
export interface Props {
emotions: Emotion[];
}
class EmotionsPicker extends PureComponent<Props> {
keyExtractor = (item, index) => index;
renderItem = ({ item }) => (
<ListItem title={item.name} checkmark={item.checked} />
);
render() {
return (
<FlatList<Emotion>
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
data={this.props.emotions}
/>
);
}
}
Unfortunately this does not work. How can I give item the type Emotion
?
Upvotes: 66
Views: 72967
Reputation: 15996
My problem was that I was importing FlatList
from react-native-gesture-handler
instead of react-native
. The type inference didn't work properly in that scenario, but worked fine when I fixed the import.
The type of the item passed to renderItem
should be inferrable from the type of the data
array when properly done, and you shouldn't have to specify/override types.
Upvotes: 3
Reputation: 872
Founded amazing way to solve this problem
import { ListRenderItemInfo } from 'react-native'
interface PlayerDataStructure {
imageUrl: string;
doubleValue: string | number;
singleValue: string | number;
isFavorite: boolean;
name: string;
thirdRowFirstLabel: string;
thirdRowSecondLabel: string;
thirdRowThirdLabel: string;
thirdRowFourthLabel: string;
id: number
}
const player: Array<PlayerDataStructure> = [
{ imageUrl: imageUrl, doubleValue: '43', singleValue: '01', isFavorite: true, name: 'Jerome Bell', thirdRowFirstLabel: '19 y', thirdRowSecondLabel: '183 cm', thirdRowThirdLabel: '75 kg', thirdRowFourthLabel: 'Right (2HB)', id: 0 },
{ imageUrl: imageUrl, doubleValue: '43', singleValue: '01', isFavorite: false, name: 'Jerome Bell', thirdRowFirstLabel: '19 y', thirdRowSecondLabel: '183 cm', thirdRowThirdLabel: '75 kg', thirdRowFourthLabel: 'Right (2HB)', id: 1 },
];
// Render Item
const renderPlayerData = (object: ListRenderItemInfo<PlayerDataStructure>) => {
const { item } = object;
return (
<PlayerCard imageUrl={item.imageUrl} doubleValue={item.doubleValue} singleValue={item.singleValue} isFavorite={item.isFavorite} name={item.name} thirdRowFirstLabel={item.thirdRowFirstLabel} thirdRowSecondLabel={item.thirdRowSecondLabel} thirdRowThirdLabel={item.thirdRowThirdLabel} thirdRowFourthLabel={item.thirdRowFourthLabel} />
)
}
<FlatList
numColumns={2}
data={player}
keyExtractor={item => item.id.toString()}
renderItem={renderPlayerData}
contentContainerStyle={{ gap: PLAYER_CARD_VERTICAL_PADDING, paddingTop: 67 }}
columnWrapperStyle={{ justifyContent: 'space-between' }}
/>
Upvotes: 2
Reputation: 83
With TypeScript use this to avoid Type Error
keyExtractor={(item, index) => index.toString()}
In FlatList
<FlatList
data={list}
keyExtractor={(item, index) => index.toString()}
....
/>
The Type of index is Number and FlatList requires a string type value as its keys.
Upvotes: 2
Reputation: 7109
I feel like the answers here only address how to in better cases fix and in worse cases bypass the type checking, but fail to explain why it does not work or why it should be typed the way it is.
I'd like to provide some more clarity as to why this fails for most people as it's very poorly documented even on the official react-native wiki.
The component FlatList
method does not expect a render function you are commonly used for passing (i.e.) standard react element constructor (props) => JSX.Element
.
For better or for worse, FlatList
's renderItem
method is typed the following way:
type ListRenderItem<ItemT> = (info: ListRenderItemInfo<ItemT>) =>
React.ReactElement | null;
This is because it does not expect a direct element constructor as you would standardly construct renderItem
methods as you are used to in most React patterns.
Instead it expects a callback with a specific type. This is for internal handling of optimalization purposes, but it still is rather clunky when it comes to typing.
export interface ListRenderItemInfo<ItemT> {
item: ItemT;
index: number;
separators: {
highlight: () => void;
unhighlight: () => void;
updateProps: (select: 'leading' | 'trailing', newProps: any) => void;
};
}
So it means we should treat our own internal renderItem
as a callback as well with specific item prop where we pass the specific <ItemT>
generic
type Props = {
emotions: Emotion[]
}
// This is the standard React function component
const Item = (({ title, checked }): Emotion) => (
<ListItem title={title} checked={checked} />
)
// This is the callback for constructing the function component
const renderItem: ListRenderItem<Emotion> = ({ item }: ListRenderItemInfo<Emotion>) => (
<Item title={item.title} checked={item.checked} />
)
As you can see, this might be fairly confusing on first glance and easy to mix-up. In the ListRenderItem<T>
tells typescript, we're looking to create a function of type T
(generic argument) which then constructs (returns) a Reat.ReactElement | null
. But since React Components themselves are functions, we still need to pass the function to create one. This is where the confusion and the type error stems from.
This essentially
ListRenderItem<T>
ListRenderItemInfo<T>
with the prop item
(props: T) => JSX.Element
instead of the the standard way of passing props and creating element from it. That's also why you're (were) getting the following typescript errors:
Property 'property name' does not exist on ListRenderItem<any>
Upvotes: 5
Reputation: 316
const renderItem = ( {item}:{item:{ id:number, name:string , check:boolean }}) => { return ( ) }
Upvotes: 0
Reputation: 67
this solution works perfectly fine for me
interface Props {
emotions: Emotion[];
}
class EmotionsPicker extends React.Component<Props> {
render(): React.ReactNode {
return (
<FlatList<Emotion>
data={this.props.emotions}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
/>
);
}
renderItem: ListRenderItem<Emotion> = (props: ListRenderItemInfo<Emotion>): ReactElement<Emotion, string | JSXElementConstructor<Emotion>> => {};
keyExtractor = (item: Emotion): string => {};
}
Upvotes: -1
Reputation: 338
I found a solution that worked pretty well for me:
import { FlatList, ListRenderItemInfo } from 'react-native';
<FlatList
data={data}
renderItem={({ item }: ListRenderItemInfo<ItemType>) => (
<ItemComponent item={item} />
)}
keyExtractor={(item: ItemType) => item.id}
/>
Upvotes: 21
Reputation: 71
If not an inline function, the correct type exported from React for this when destructured is ListRenderItemInfo<ItemT>
:
export interface ListRenderItemInfo<ItemT> {
item: ItemT;
index: number;
separators: {
highlight: () => void;
unhighlight: () => void;
updateProps: (select: "leading" | "trailing", newProps: any) => void;
};
}
Upvotes: 7
Reputation: 1317
I know it's an old question but people googling it might still appreciate it.
import { FlatList, ListRenderItem } from 'react-native'
/*
.
.
.
*/
renderItem: ListRenderItem<Emoticon> = ({ item }) => (
<ListItem title={item.name} checkmark={item.checked} />
);
Upvotes: 112
Reputation: 175078
This is because at the time renderItem
is defined, TypeScript has no way of knowing it's supposed to go as the renderItem
prop of FlatList
.
If you had skipped the variable and directly plopped the function in the prop, TypeScript should be able to infer it correctly:
export interface Props {
emotions: Emotion[];
}
class EmotionsPicker extends PureComponent<Props> {
render() {
return (
<FlatList<Emotion>
keyExtractor={(item, index) => index}
renderItem={({ item }) => <ListItem title={item.name} checkmark={item.checked} />}
data={this.props.emotions}
/>
);
}
}
Upvotes: 28
Reputation: 14814
I found a solution (though I don't know if it's ideal):
renderItem = ({ item }: { item: Emotion }) => (
<ListItem title={item.name} checkmark={item.chosen} />
);
Upvotes: 72