RN FlatList with Typescript and Styled Components
Asked Answered
C

6

10

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?

Chiropractic answered 21/10, 2020 at 8:56 Comment(0)
C
0

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}
/>
Chiropractic answered 22/10, 2020 at 8:38 Comment(6)
i don't have an IDE handy but TypeScript should 'know' the type of item already, so you should be able to omit all the type information from the renderItem and keyExtractor params. e.g. renderItem={ ({ item }) => ( keyExtractor={item => .Fulks
Yes, I thought exactly the same. Unfortunately, it doesn't. As I mentioned before, if I use RN FlatList TS doesn't complain and infers the type but it complains when I use styled-components, although I have @types/styled-components installed.Chiropractic
ah - yes I see. I remember this one from a couple years back, we ran into just the same thing. what if you define const StyledFlatList = styled.FlatList' background-color: 'white'; ' as unknown as FlatList. E.g. make TS think StyledFlatList is just a 'plain' FlatList?Fulks
Btw, can you elaborate on the syntax? const StyledFlatList = styled(FlatList)' background-color: ${colors.silver}; ' as unknown as FlatList doesn't workChiropractic
sure sorry I couldn't put the correct syntax in the comment... try putting as unknown as FlatList after the declaration of StyledFlatList. This forces TS to think that StyledFlatList is of type FlatList.Fulks
Hi @BenClayton, I have added an update below because it formats better. I get a new TS error now JSX element type 'StyledFlatList' does not have any construct or call signatures.Chiropractic
E
17

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}
/>

Enswathe answered 5/2, 2021 at 1:47 Comment(1)
const StyledFlatList = styled.FlatList` background-color: 'white'; ` as typeof FlatList; Works as wellBombycid
V
6

This simple solution works for me:

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

const StyledFlatList = styled(FlatList as new () => FlatList<IProduct>)`
  background-color: #f7f7f7;
`
Viator answered 15/5, 2021 at 11:28 Comment(0)
P
3

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;
`;
Panta answered 18/9, 2022 at 23:39 Comment(0)
A
1

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%;
`;
Agra answered 27/2, 2023 at 12:35 Comment(0)
C
0

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}
/>
Chiropractic answered 22/10, 2020 at 8:38 Comment(6)
i don't have an IDE handy but TypeScript should 'know' the type of item already, so you should be able to omit all the type information from the renderItem and keyExtractor params. e.g. renderItem={ ({ item }) => ( keyExtractor={item => .Fulks
Yes, I thought exactly the same. Unfortunately, it doesn't. As I mentioned before, if I use RN FlatList TS doesn't complain and infers the type but it complains when I use styled-components, although I have @types/styled-components installed.Chiropractic
ah - yes I see. I remember this one from a couple years back, we ran into just the same thing. what if you define const StyledFlatList = styled.FlatList' background-color: 'white'; ' as unknown as FlatList. E.g. make TS think StyledFlatList is just a 'plain' FlatList?Fulks
Btw, can you elaborate on the syntax? const StyledFlatList = styled(FlatList)' background-color: ${colors.silver}; ' as unknown as FlatList doesn't workChiropractic
sure sorry I couldn't put the correct syntax in the comment... try putting as unknown as FlatList after the declaration of StyledFlatList. This forces TS to think that StyledFlatList is of type FlatList.Fulks
Hi @BenClayton, I have added an update below because it formats better. I get a new TS error now JSX element type 'StyledFlatList' does not have any construct or call signatures.Chiropractic
S
0

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}
/>
Scarlett answered 28/5, 2023 at 10:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.