Thomas Dittmar
Thomas Dittmar

Reputation: 1924

RN FlatList with Typescript and Styled Components

SO community,

I can't figure out the correct type definition for an RN FlatList styled with Styled Components in Typescript

So I have type IProduct like that

interface IProduct {
  id: string;
  name: string;
}

and I define the types for the FlatList like that

<FlatList
  data={products}
  renderItem={({ item }: { item: IProduct }) => (
    <SomeComponent item={item} />
  )}
  keyExtractor={(item: IProduct) => item.id}
/>

everything works fine. Typescript does not complain but as soon as I want to style the FlatList like that

const StyledFlatList = styled.FlatList`
  background-color: 'white';
`;

<StyledFlatList
  data={products}
  renderItem={({ item }: { item: IProduct }) => (
    <SomeComponent item={item} />
  )}
  keyExtractor={(item: IProduct) => item.id}
/>

I get lots of Typescript errors

No overload matches this call.
  Overload 2 of 2, '(props: StyledComponentPropsWithAs<typeof FlatList, DefaultTheme, {}, never>): ReactElement<StyledComponentPropsWithAs<typeof FlatList, DefaultTheme, {}, never>, string | ... 1 more ... | (new (props: any) => Component<...>)>', gave the following error.
    Type '({ item }: { item: IProduct; }) => JSX.Element' is not assignable to type 'ListRenderItem<unknown>'.
  Overload 2 of 2, '(props: StyledComponentPropsWithAs<typeof FlatList, DefaultTheme, {}, never>): 
ReactElement<StyledComponentPropsWithAs<typeof FlatList, DefaultTheme, {}, never>, string | ... 1 more ... | (new (props: any) => Component<...>)>', gave the following error.
    Type '(item: IProduct) => string' is not assignable to type '(item: unknown, index: number) => string'.ts(2769)

index.d.ts(4218, 5): The expected type comes from property 'keyExtractor' which is declared here on type 'IntrinsicAttributes & Pick<Pick<FlatListProps<unknown> & RefAttributes<FlatList<unknown>>, "ref" | "data" | "style" | "ItemSeparatorComponent" | ... 141 more ... | "key"> & Partial<...>, "ref" | ... 144 more ... | "key"> & { ...; } & { ...; } & { ...; }'

index.d.ts(4218, 5): The expected type comes from property 'keyExtractor' which is declared here on type 'IntrinsicAttributes & Pick<Pick<FlatListProps<unknown> & RefAttributes<FlatList<unknown>>, "ref" | "data" | "style" | "ItemSeparatorComponent" | ... 141 more ... | "key"> & Partial<...>, "ref" | ... 144 more ... | "key"> & { ...; } & { ...; } & { ...; }'

Can someone tell me how to fix that error?

Upvotes: 11

Views: 10625

Answers (6)

moeabdol
moeabdol

Reputation: 5059

You can define a custom styled-components FlatList type that accepts IProduct as follows:

import styled from 'styled-components';
import { FlatList, FlatListProps } from 'react-native';
import IProduct from './Product.d';

export const StyledFlatList = styled(FlatList as new (props: FlatListProps<IProduct>) => FlatList<IProduct>)`
  // your styles
`;

Then use it in your .tsx

<StyledFlatList
  data={products}
  renderItem={({ item }) => <SomeComponent item={item} />}
  keyExtractor={(item: IProduct) => item.id}
/>

Upvotes: 0

GSFZamai
GSFZamai

Reputation: 74

This is how it worked for me, after some tests (and mergin the sugestions on this topic):

At styles.ts, my typed FlatList ended like this:

 export const CustomFlatList = styled(FlatList as typeof FlatList<EstablishmentTransactionAntecipationDTO>)`
  flex: 1;
  width: 100%;
`;

Upvotes: 1

David
David

Reputation: 31

I think you just need to inform the generic type with, styled.FlatList``

import styled from 'styled-components/native';

const StyledFlatList = styled.FlatList<IProduct>`
  background-color: #f7f7f7;
`;

Upvotes: 3

Sam Schooler
Sam Schooler

Reputation: 385

Looks like adding typeof to another suggested solution solved this in a more straightforward way, even allowing you to exclude types from props:

const StyledFlatList = (styled.FlatList`
  background-color: 'white';
` as unknown) as typeof FlatList;

<StyledFlatList
  data={products}
  renderItem={({ item }) => (
    <SomeComponent item={item} />
  )}
  keyExtractor={(item) => item.id}
/>

Upvotes: 18

Edicarlos Lopes
Edicarlos Lopes

Reputation: 1141

This simple solution works for me:

interface IProduct {
  id: string;
  name: string;
}

const StyledFlatList = styled(FlatList as new () => FlatList<IProduct>)`
  background-color: #f7f7f7;
`

Upvotes: 6

Thomas Dittmar
Thomas Dittmar

Reputation: 1924

After a few trial and error runs I think I found a solution that works but it's not ideal.

<StyledFlatList<any>
  data={products}
  renderItem={({ item }: { item: IProduct }) => (
    <ProductCard product={item} onProductPress={handleOnProductPress} />
  )}
  keyExtractor={(item: IProduct) => item.id}
/>

Let me know if that looks OK with you.

UPDATE 1:

If I add the code snippet Ben Clayton suggested, prettier will format the code like that

const StyledFlatList = (styled.FlatList`
  background-color: ${colors.silver};
` as unknown) as FlatList;

and I get a TS error

JSX element type 'StyledFlatList' does not have any construct or call signatures.

UPDATE 2:

Instead of <any> I tried <React.ElementType> as suggested here which seems to work too.

<StyledFlatList<React.ElementType>
  data={products}
  renderItem={({ item }: { item: IProduct }) => (
    <ProductCard product={item} onProductPress={handleOnProductPress} />
  )}
  keyExtractor={(item: IProduct) => item.id}
/>

Upvotes: 0

Related Questions